目录
| 类别 | 描述 |
|---|---|
| 信号的定义 | 信号是事件发生时对进程的通知机制,可以理解为软件中断,通常用于处理异步事件。 |
| 信号与硬件中断的相似性 | 信号与硬件中断相似,能够打断程序的正常流程,通知进程处理特定的事件。 |
| 信号的作用 | 用于进程间通信(IPC)和同步。信号通过通知进程某些事件的发生,进而影响进程的行为。 |
信号产生的条件
| 条件 | 说明 |
|---|---|
| 硬件异常 | 例如除数为 0、访问非法内存等,硬件检测到异常后由内核向进程发送信号。 |
| 终端输入 | 用户通过按键(如 CTRL + C、CTRL + Z 等)触发的信号。 |
进程调用 kill() |
进程可以使用 kill() 系统调用向另一个进程发送信号,前提是进程具有合适的权限。 |
用户使用 kill 命令 |
用户在终端使用 kill 命令发送信号给其他进程,例如使用 kill -9 <pid> 来终止进程。 |
| 软件事件 | 进程执行过程中,系统检测到某些事件发生时(如定时器超时、CPU 时间限制等),内核向进程发送信号。 |
信号的处理方式
| 处理方式 | 说明 |
|---|---|
| 忽略信号 | 进程选择忽略某些信号。大多数信号可以被忽略,但 SIGKILL 和 SIGSTOP 不能被忽略。忽略某些硬件异常信号会导致未定义的行为。 |
| 捕获信号 | 进程注册一个自定义的信号处理函数,在信号到达时,内核会调用该函数处理信号。可以使用 signal() 系统调用来注册信号处理函数。 |
| 执行系统默认操作 | 当进程不处理信号时,系统会执行该信号的默认操作。大多数信号的默认操作是终止进程,除非是 SIGKILL 或 SIGSTOP。 |
信号的异步性质
| 特性 | 说明 |
|---|---|
| 异步事件 | 信号是异步事件,进程无法预测信号的确切时间,信号的到来是随机的,类似于硬件中断。进程无法通过简单的变量测试或系统调用判断是否会发生信号,只有当信号到达时,才会中断当前进程的执行。 |
/* Signals. */
#define SIGHUP 1 /* Hangup (POSIX). */
#define SIGINT 2 /* Interrupt (ANSI). */
#define SIGQUIT 3 /* Quit (POSIX). */
#define SIGILL 4 /* Illegal instruction (ANSI). */
#define SIGTRAP 5 /* Trace trap (POSIX). */
#define SIGABRT 6 /* Abort (ANSI). */
#define SIGIOT 6 /* IOT trap (4.2 BSD). */
#define SIGBUS 7 /* BUS error (4.2 BSD). */
#define SIGFPE 8 /* Floating-point exception (ANSI). */
#define SIGKILL 9 /* Kill, unblockable (POSIX). */
#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */
#define SIGSEGV 11 /* Segmentation violation (ANSI). */
#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */
#define SIGPIPE 13 /* Broken pipe (POSIX). */
#define SIGALRM 14 /* Alarm clock (POSIX). */
#define SIGTERM 15 /* Termination (ANSI). */
#define SIGSTKFLT 16 /* Stack fault. */
#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */
#define SIGCHLD 17 /* Child status has changed (POSIX). */
#define SIGCONT 18 /* Continue (POSIX). */
#define SIGSTOP 19 /* Stop, unblockable (POSIX). */
#define SIGTSTP 20 /* Keyboard stop (POSIX). */
#define SIGTTIN 21 /* Background read from tty (POSIX). */
#define SIGTTOU 22 /* Background write to tty (POSIX). */
#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */
#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */
#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */
#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */
#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */
#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */
#define SIGPOLL SIGIO /* Pollable event occurred (System V). */
| 信号名称 | 说明 | 默认操作 |
|---|---|---|
| SIGINT | 当用户按下 CTRL + C 时,内核将发送该信号给前台进程组中的进程。 | 终止进程 |
| SIGQUIT | 当用户按下 CTRL + \ 时,内核将发送该信号给前台进程组中的进程。 | 终止进程并生成核心转储文件 |
| SIGILL | 进程执行非法的机器指令时,内核发送该信号。 | 终止进程 |
| SIGABRT | 当进程调用 abort() 系统调用时,发送该信号。 |
终止进程并生成核心转储文件 |
| SIGBUS | 进程发生总线错误(内存访问错误)时,内核发送该信号。 | 终止进程 |
| SIGFPE | 进程发生算术错误时(如除零),内核发送该信号。 | 终止进程 |
| SIGKILL | 进程无法阻塞、忽略或捕获该信号。用于强制终止进程。 | 终止进程 |
| SIGUSR1 | 用户自定义信号,通常用于进程间通信或同步。 | 终止进程 |
| SIGSEGV | 进程对无效内存引用时,内核发送该信号。 | 终止进程 |
| SIGUSR2 | 用户自定义信号,通常与 SIGUSR1 一样使用。 | 终止进程 |
| SIGPIPE | 当进程向已关闭的管道、FIFO 或套接字写入时,内核发送该信号。 | 终止进程 |
| SIGALRM | 当定时器到期时,内核通过 alarm() 或 setitimer() 系统调用发送该信号。 |
终止进程 |
| SIGTERM | 默认终止进程的信号,也常通过 kill 命令发送。 |
终止进程(进程可捕获并清理资源) |
| SIGCHLD | 当子进程终止或状态发生变化时,内核发送该信号给父进程。 | 忽略信号 |
| SIGCLD | 同 SIGCHLD,是同义词。 |
忽略信号 |
| SIGCONT | 发送给停止状态的进程,恢复其运行。 | 恢复进程运行(如果进程处于停止状态) |
| SIGSTOP | 强制停止进程,无法被忽略或捕获。 | 停止进程 |
| SIGTSTP | 当用户按下 CTRL + Z 时,内核将发送该信号使进程停止。 | 停止进程 |
| SIGXCPU | 当进程的 CPU 时间超出资源限制时,内核发送该信号。 | 终止进程 |
| SIGVTALRM | 应用程序通过 setitimer() 设置虚拟定时器时,定时器到期时发送该信号。 |
终止进程 |
| SIGWINCH | 当终端窗口尺寸发生变化时(例如用户调整大小或应用程序调用 ioctl() 设置),内核发送该信号。 |
终止进程 |
| SIGPOLL/SIGIO | 当异步 I/O 事件发生时,内核发送该信号。 | 终止进程 |
| SIGSYS | 当进程发起的系统调用错误时,内核发送该信号。 | 终止进程 |
信号的一些常见的操作
signal()
原型:
void (*signal(int signum, void (*handler)(int)))(int);
说明:
-
signal()用于指定一个信号的处理函数,当信号到达时,操作系统会调用该处理函数。 -
第一个参数
signum是需要处理的信号的编号(如SIGINT,SIGTERM等)。 -
第二个参数handler是信号处理函数的指针,用户可以自定义该函数以处理特定信号。
-
如果
handler为SIG_IGN,表示忽略该信号。 -
如果
handler为SIG_DFL,表示恢复默认的信号处理行为。
-
-
返回值是之前信号处理函数的地址,可以用来恢复原始信号处理。
注意:
-
signal()是一个相对简化的接口,不适合处理复杂的信号处理需求,且其行为在不同的系统中可能有所不同。 -
在现代系统中,推荐使用
sigaction()替代signal(),因为sigaction()提供了更多的控制选项。
sigaction()
原型:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
说明:
-
sigaction()用于更精细地控制信号的处理。它允许用户指定信号处理程序、处理程序的行为、以及如何恢复原来的信号处理行为。 -
第一个参数
signum是信号编号。 -
第二个参数act是一个指向struct sigaction的指针,该结构体定义了信号处理程序的详细信息。
-
sa_handler:信号处理函数的指针。 -
sa_mask:一个信号集,表示在信号处理程序执行期间要阻塞的信号。 -
sa_flags:指定信号处理程序的行为。常见的标志包括SA_RESTART(使得系统调用在被信号打断后自动重启)和SA_SIGINFO(启用额外的信号信息传递)。
-
-
第三个参数
oldact是一个指向struct sigaction的指针,用于保存原来的信号处理设置,以便稍后恢复。
返回值:
- 成功时返回
0,失败时返回-1,并设置errno。
raise()
原型:
int raise(int signum);
说明:
-
raise()用于向当前进程发送一个信号。它将信号发送到当前进程的进程组中。 -
参数
signum是要发送的信号编号。 -
返回值:成功时返回
0,失败时返回-1,并设置errno。
kill()
原型:
int kill(pid_t pid, int signum);
说明:
-
kill()用于向指定进程或进程组发送信号。即使是向自己的进程发送信号,它也能有效地触发信号的处理。 -
参数pid是目标进程的 PID:
-
如果
pid > 0,发送信号给指定的进程。 -
如果
pid == 0,发送信号给与当前进程属于同一进程组的所有进程。 -
如果
pid == -1,发送信号给所有进程(需要超级用户权限)。 -
如果
pid < -1,发送信号给指定进程组的所有进程。
-
-
参数
signum是信号编号(如SIGINT,SIGTERM等)。 -
返回值:成功时返回
0,失败时返回-1,并设置errno。
sigprocmask()
原型:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
说明:
-
sigprocmask()用于控制进程的信号屏蔽字,屏蔽字表示进程在处理信号时哪些信号应被暂时阻塞。 -
参数how定义了操作方式:
-
SIG_BLOCK:将信号集中的信号加入到当前的信号屏蔽字中。 -
SIG_UNBLOCK:从当前屏蔽字中删除信号集中的信号。 -
SIG_SETMASK:将信号集设置为指定的信号集。
-
-
参数
set是一个信号集,包含要屏蔽或解除屏蔽的信号。 -
参数
oldset用于保存操作前的信号屏蔽字。
sigpending()
原型:
int sigpending(sigset_t *set);
说明:
-
sigpending()用于检查当前进程是否有被屏蔽但尚未处理的信号。 -
参数
set是一个信号集,函数会将所有挂起的信号保存到该信号集。 -
返回值:成功时返回
0,失败时返回-1,并设置errno。
sigwait()
原型:
int sigwait(const sigset_t *set, int *signo);
说明:
-
sigwait()用于同步地等待一个指定信号集中的信号到达,并阻塞直到接收到该信号。 -
参数
set是一个信号集,指定了进程要等待的信号。 -
参数
signo用于返回接收到的信号编号。 -
返回值:返回
0表示成功,返回-1表示错误,errno设置为相应的错误码。