信号会中断一个正在执行的程序,在这种意义下,就是用信号与这个程序进行通信。大多数的信号要么可以被忽略(阻塞)或者被处理(通过特别设计的代码)。SIGSTOP (暂停)和 SIGKILL (立即停止)是最应该提及的两种信号。这种符号常量有整数类型的值,例如 SIGKILL 对应的值为 9 。
信号可以在与用户交互的情况下发生。例如,一个用户从命令行中敲了 Ctrl+C 来终止一个从命令行中启动的程序;Ctrl+C 将产生一个 SIGTERM 信号。SIGTERM 意即终止,它可以被阻塞或者被处理,而不像 SIGKILL 信号那样。一个进程也可以通过信号和另一个进程通信,这样使得信号也可以作为一种 IPC 机制。
考虑一下一个多进程应用,例如 Nginx 网络服务器是如何被另一个进程优雅地关闭的。kill 函数:
int kill(pid_t pid, int signum); /* declaration */
可以被一个进程用来终止另一个进程或者一组进程。假如 kill 函数的第一个参数是大于 0 的,那么这个参数将会被认为是目标进程的 pid (进程 ID),假如这个参数是 0 ,则这个参数将会被视作信号发送者所属的那组进程。
kill 的第二个参数要么是一个标准的信号数字(例如 SIGTERM 或 SIGKILL ),要么是 0 ,这将会对信号做一次询问,确认第一个参数中的 pid 是否是有效的。这样优雅地关闭一个多进程应用就可以通过向组成该应用的一组进程发送一个终止信号来完成,具体来说就是调用一个 kill 函数,使得这个调用的第二个参数是 SIGTERM 。(Nginx 主进程可以通过调用 kill 函数来终止其他工人进程,然后再停止自己。)就像许多库函数一样,kill 函数通过一个简单的可变语法拥有更多的能力和灵活性。
示例 3. 一个多进程系统的优雅停止
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> -
void graceful(int signum) { printf("\tChild confirming received signal: %i\n", signum); puts("\tChild about to terminate gracefully..."); sleep(1); puts("\tChild terminating now..."); _exit(0); /* fast-track notification of parent */ } -
void set_handler() { struct sigaction current; sigemptyset(¤t.sa_mask); /* clear the signal set */ current.sa_flags = 0; /* enables setting sa_handler, not sa_action */ current.sa_handler = graceful; /* specify a handler */ sigaction(SIGTERM, ¤t, NULL); /* register the handler */ } -
void child_code() { set_handler(); -
while (1) { /` loop until interrupted `/ sleep(1); puts("\tChild just woke up, but going back to sleep."); } } -
void parent_code(pid_t cpid) { puts("Parent sleeping for a time..."); sleep(5); -
/* Try to terminate child. */ if (-1 == kill(cpid, SIGTERM)) { perror("kill"); exit(-1); } wait(NULL); /` wait for child to terminate `/ puts("My child terminated, about to exit myself..."); } -
int main() { pid_t pid = fork(); if (pid < 0) { perror("fork"); return -1; /* error */ } if (0 == pid) child_code(); else parent_code(pid); return 0; /* normal */ }
(编辑:ASP站长网)
|