There are many ways for processes to communicate on Unix. This post introduces a simple way to use signals. You can first read Beej’s introduction. As the name suggests, a signal is a notification sent and received by processes. For example, when you use a shell, Ctrl-C can interrupt a program because the shell catches the SIGINT signal triggered by Ctrl-C, recognizes it as an interrupt signal, and terminates the program.

To send signals, you can use sigaction() or signal(). I recommend sigaction because it’s newer. For a detailed comparison, see the Stack Overflow thread “What is the difference between sigaction and signal?”.

If you want to trigger a signal, you can use kill() or sigqueue(). The difference is that the latter is Linux-only, but it lets you attach additional information via siginfo. In addition, some system calls can trigger signals as well. For example, if you try to send() to a non-existent socket, you’ll get a SIGPIPE.

Below is a simple example. If you want to understand the full set of parameters, you still need to read the man pages:

// signal.cc
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {
  // 宣告 sigaction 物件
  struct sigaction act;
  memset(&act, 0, sizeof act);

  // 建立 signal handler,應該盡可能簡化 handler 要做的事情
  // 並且要小心 signal 執行時,原程式已經在做了一樣的事情
  act.sa_sigaction = [](int signo, siginfo_t *info, void *context) {
    if (signo == SIGINT) { // 接收 Ctrl-C
      printf("Received SIGINT.\n");
    } else if (signo == SIGUSR1) { // 接收數字
      int int_val = info->si_value.sival_int;
      printf("recieve: %d\n", int_val);
    } else if (signo == SIGUSR2) { // 接收指標
      void *ptr = info->si_value.sival_ptr;
      printf("recieve: %p\n", ptr);
    }
  };

  // sa_sigaction 必須搭配 SA_SIGINFO,否則會呼叫 act.sa_handler
  // 但我們要 sa_sigaction 才能接收 siginfo
  //
  // 其他參數:
  //  重新啟動可被中斷的 system call 要設 SA_RESTART
  //  SIGCHLD 可以設 SA_NOCLDWAIT 來避免疆屍
  act.sa_flags = SA_SIGINFO;

  // 設定 SIGUSR1
  if (0 != sigaction(SIGUSR1, &act, NULL)) {
    perror("sigaction () failed installing SIGUSR1 handler");
    return EXIT_FAILURE;
  }
  // 設定 SIGUSR2
  if (0 != sigaction(SIGUSR2, &act, NULL)) {
    perror("sigaction() failed installing SIGUSR2 handler");
    return EXIT_FAILURE;
  }

  // 設定 SIGINT
  if (0 != sigaction(SIGINT, &act, NULL)) {
    perror("sigaction() failed installing SIGUSR2 handler");
    return EXIT_FAILURE;
  }

  // 執行 15 秒,過程中可以嘗試按 Ctrl-C
  for (int i = 1; i <= 15; i++) {
    // 第二秒觸發 SIGUSR1 並送出 123
    if (i == 2) {
      union sigval value;

      // 目標 process 的 pid,這邊要送給自己,為自己的 pid
      int pid = getpid();

      // sigval 是 union,sival_int 或 sival_ptr 只能擇一
      value.sival_int = 123;

      if (sigqueue(pid, SIGUSR1, value) == 0) {
        printf("signal sent successfully!!\n");
      } else {
        perror("SIGSENT-ERROR:");
      }
    }

    // 第二秒觸發 SIGUSR2 並送出 act 的地址
    if (i == 4) {
      union sigval value;
      pid_t pid = getpid();

      value.sival_ptr = &act;
      if (sigqueue(pid, SIGUSR2, value) == 0) {
        printf("signal sent successfully!!\n");
      } else {
        perror("SIGSENT-ERROR:");
      }
    }

    // 第二秒觸發 SIGUSR1 並送出 3333
    if (i == 6) {
      union sigval value;
      int pid = getpid();

      value.sival_int = 3333;
      if (sigqueue(pid, SIGUSR1, value) == 0) {
        printf("signal sent successfully!!\n");
      } else {
        perror("SIGSENT-ERROR:");
      }
    }

    printf("Tick #%d.\n", i);

    sleep(1);
  }

  return EXIT_SUCCESS;
}

The output looks like this:

$ g++ signal.cc
$ ./a.out
Tick #1.
recieve: 123
signal sent successfully!!
Tick #2.
Tick #3.
^CReceived SIGINT.
recieve: 0x7ffde08e7970
signal sent successfully!!
Tick #4.
Tick #5.
^CReceived SIGINT.
recieve: 3333
signal sent successfully!!
Tick #6.
^CReceived SIGINT.
Tick #7.
Tick #8.
^CReceived SIGINT.
Tick #9.
^CReceived SIGINT.
Tick #10.
Tick #11.
^CReceived SIGINT.
Tick #12.
Tick #13.
Tick #14.
Tick #15.