概念

  • 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
  • 信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。
  • 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被 取消时才被传递给进程。

用户进程对信号的响应方式:

  • 忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即 SIGKILL 及 SIGSTOP。
  • 捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。
  • 执行缺省操作:Linux对每种信号都规定了默认操作

标准信号

linux 支持一些标准信号,以及它们的默认操作。

1
Signal      Standard   Action   Comment
────────────────────────────────────────────────────────────────────────
SIGABRT      P1990      Core    Abort signal from abort(3)
SIGALRM      P1990      Term    Timer signal from alarm(2)
SIGBUS       P2001      Core    Bus error (bad memory access)
SIGCHLD      P1990      Ign     Child stopped or terminated
SIGCLD         -        Ign     A synonym for SIGCHLD
SIGCONT      P1990      Cont    Continue if stopped
SIGEMT         -        Term    Emulator trap
SIGFPE       P1990      Core    Floating-point exception

SIGHUP       P1990      Term    Hangup detected on controlling terminal
                                   or death of controlling process
SIGILL       P1990      Core    Illegal Instruction
SIGINFO        -                A synonym for SIGPWR
SIGINT       P1990      Term    Interrupt from keyboard
SIGIO          -        Term    I/O now possible (4.2BSD)
SIGIOT         -        Core    IOT trap. A synonym for SIGABRT
SIGKILL      P1990      Term    Kill signal
SIGLOST        -        Term    File lock lost (unused)
SIGPIPE      P1990      Term    Broken pipe: write to pipe with no
                                   readers; see pipe(7)
SIGPOLL      P2001      Term    Pollable event (Sys V);
                                   synonym for SIGIO
SIGPROF      P2001      Term    Profiling timer expired
SIGPWR         -        Term    Power failure (System V)
SIGQUIT      P1990      Core    Quit from keyboard
SIGSEGV      P1990      Core    Invalid memory reference
SIGSTKFLT      -        Term    Stack fault on coprocessor (unused)
SIGSTOP      P1990      Stop    Stop process
SIGTSTP      P1990      Stop    Stop typed at terminal
SIGSYS       P2001      Core    Bad system call (SVr4);
                                   see also seccomp(2)
SIGTERM      P1990      Term    Termination signal
SIGTRAP      P2001      Core    Trace/breakpoint trap
SIGTTIN      P1990      Stop    Terminal input for background process
SIGTTOU      P1990      Stop    Terminal output for background process
SIGUNUSED      -        Core    Synonymous with SIGSYS
SIGURG       P2001      Ign     Urgent condition on socket (4.2BSD)
SIGUSR1      P1990      Term    User-defined signal 1
SIGUSR2      P1990      Term    User-defined signal 2
SIGVTALRM    P2001      Term    Virtual alarm clock (4.2BSD)
SIGXCPU      P2001      Core    CPU time limit exceeded (4.2BSD);
                                   see setrlimit(2)
SIGXFSZ      P2001      Core    File size limit exceeded (4.2BSD);
                                   see setrlimit(2)
SIGWINCH       -        Ign     Window resize signal (4.3BSD, Sun)

下面是默认操作的含义:

  • Term 默认操作是终止进程.
  • Ign 默认操作是忽略信号.
  • Core 默认操作是终止进程并 dump core 文件.
  • Stop 默认操作是停止进程.
  • Cont 默认操作是如果进程停止了,继续它的执行

可以通过 sigaction(2) 或者 signal(2) 函数修改进程对信号的默认行为。

相关函数

发送信号

  • raise(3) 向调用此函数的线程发送信号
  • kill(2) 向指定的进程、进程组或者系统中所有进程发送信号
  • pidfd_send_signal(2) 向一个通过 PID fd 指定的进程发送信号
  • killpg(3) 向一个进程组中的所有进程发送信号
  • pthread_kill(3) 向一个同进程的指定的 POSIX 线程发送信号
  • tgkill(2) 向指定进程的指定线程发送信号(pthread_kill基于此内核函数实现)
  • sigqueue(3) 将实时信号和一部分附带数据发送到指定的进程

等待信号捕获

下面的函数会暂停调用它们的线程,直到收到一个信号,或者一个未处理的信号终止进程。

  • pause(2) 暂停执行直到任意信号被捕获
  • sigsuspend(2) 暂时修改信号 mask,并暂停执行,直到 mask 之外的信号被捕获

同步接收信号

除了使用 sigaction 等函数设置信号回调之外,还可以通过同步等待接收信号的方式来处理信号。

  • sigwaitinfo(2)、sigtimedwait(2)、sigwait(3) 会暂停执行,直到被指定的信号集合中的一个被触发。信号捕获后,这几个函数都会返回捕获的信号值,
  • signalfd(2) 返回一个可以读取到接收到的信号的 fd。使用 read 从 fd 读取信号值时,阻塞进程,直到指定的信号触发。

示例代码

监听信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

void getSignalHandler(int signum) {
printf("get signal %d\n", signum);
}

void getSignalExit(int signum) {
printf("get signal %d\n", signum);
exit(0);
}

int main() {
printf("start app1\n");
printf("SIGRTMIN: %d\n", SIGRTMIN);
// 监听信号1,输出日志
struct sigaction sa_usr;
sa_usr.sa_flags = 0;
sa_usr.sa_handler = getSignalHandler; //信号处理函数
sigaction(SIGRTMIN + 1, &sa_usr, NULL);
// 监听信号2,退出进程
struct sigaction sa_user2;
sa_user2.sa_flags = 0;
sa_user2.sa_handler = getSignalExit;
sigaction(SIGRTMIN + 2, &sa_user2, NULL);
while (true)
{
printf("waiting for signal...\n");
sleep(1);
}
return 0;
}

发送信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string>
#include <signal.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
printf("start app2\n");
if (argc < 3) {
printf("Usage: %s <pid> <signal>\n", argv[0]);
return 0;
}
int pid = std::stoi(argv[1]);
int signal = std::stoi(argv[2]);
printf("send signal %d to pid %d", signal, pid);
kill(pid, signal);
// 向指定 pid 发送 指定信号
return 0;
}

参考文档:

☞ 参与评论