🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 信号捕捉 ## 信号处理方式 一个进程收到一个信号的时候,可以用如下方法进行处理: 1)执行系统默认动作 对大多数信号来说,系统默认动作是用来终止该进程。 2)忽略此信号(丢弃) 接收到此信号后没有任何动作。 3)执行自定义信号处理函数(捕获) 用用户定义的信号处理函数处理该信号。 【注意】:SIGKILL 和 SIGSTOP 不能更改信号的处理方式,因为它们向用户提供了一种使进程终止的可靠方法。 内核实现信号捕捉过程: ![](https://img.kancloud.cn/ee/40/ee40dac755d0256897cb1b82920b510a_665x489.png) ## signal函数 ~~~ #include <signal.h> ​ typedef void(*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 功能: 注册信号处理函数(不可用于 SIGKILL、SIGSTOP 信号),即确定收到信号后处理函数的入口地址。此函数不会阻塞。 ​ 参数: signum:信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令 kill - l("l" 为字母)进行相应查看。 handler : 取值有 3 种情况: SIG_IGN:忽略该信号 SIG_DFL:执行系统默认动作 信号处理函数名:自定义信号处理函数,如:func 回调函数的定义如下: void func(int signo) { // signo 为触发的信号,为 signal() 第一个参数的值 } ​ 返回值: 成功:第一次返回 NULL,下一次返回此信号上一次注册的信号处理函数的地址。如果需要使用此返回值,必须在前面先声明此函数指针的类型。 失败:返回 SIG_ERR ~~~ 该函数由ANSI定义,由于历史原因在不同版本的Unix和不同版本的Linux中可能有不同的行为。因此应该尽量避免使用它,取而代之使用sigaction函数。 ~~~ // 信号处理函数 void signal_handler(int signo) { if (signo == SIGINT) { printf("recv SIGINT\n"); } else if (signo == SIGQUIT) { printf("recv SIGQUIT\n"); } } ​ int main() { printf("wait for SIGINT OR SIGQUIT\n"); ​ /* SIGINT: Ctrl+c ; SIGQUIT: Ctrl+\ */ // 信号注册函数 signal(SIGINT, signal_handler); signal(SIGQUIT, signal_handler); ​ while (1); //不让程序结束 ​ return 0; } ~~~ ## sigaction函数 ~~~ #include <signal.h> ​ int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 功能: 检查或修改指定信号的设置(或同时执行这两种操作)。 ​ 参数: signum:要操作的信号。 act: 要设置的对信号的新处理方式(传入参数)。 oldact:原来对信号的处理方式(传出参数)。 ​ 如果 act 指针非空,则要改变指定信号的处理方式(设置),如果 oldact 指针非空,则系统将此前指定信号的处理方式存入 oldact。 ​ 返回值: 成功:0 失败:-1 ~~~ **struct sigaction结构体:** ~~~ struct sigaction { void(*sa_handler)(int); //旧的信号处理函数指针 void(*sa_sigaction)(int, siginfo_t *, void *); //新的信号处理函数指针 sigset_t sa_mask; //信号阻塞集 int sa_flags; //信号处理的方式 void(*sa_restorer)(void); //已弃用 }; ~~~ 1) sa\_handler、sa\_sigaction:信号处理函数指针,和 signal() 里的函数指针用法一样,应根据情况给sa\_sigaction、sa\_handler 两者之一赋值,其取值如下: a) SIG\_IGN:忽略该信号 b) SIG\_DFL:执行系统默认动作 c) 处理函数名:自定义信号处理函数 2) sa\_mask:信号阻塞集,在信号处理函数执行过程中,临时屏蔽指定的信号。 3) sa\_flags:用于指定信号处理的行为,通常设置为0,表使用默认属性。它可以是一下值的“按位或”组合: Ø SA\_RESTART:使被信号打断的系统调用自动重新发起(已经废弃) Ø SA\_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。 Ø SA\_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。 Ø SA\_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。 Ø SA\_RESETHAND:信号处理之后重新设置为默认的处理方式。 Ø SA\_SIGINFO:使用 sa\_sigaction 成员而不是 sa\_handler 作为信号处理函数。 **信号处理函数:** ~~~ void(*sa_sigaction)(int signum, siginfo_t *info, void *context); 参数说明: signum:信号的编号。 info:记录信号发送进程信息的结构体。 context:可以赋给指向 ucontext_t 类型的一个对象的指针,以引用在传递信号时被中断的接收进程或线程的上下文。 ~~~ ~~~ void myfunc(int sig) { printf("hello signal: %d\n", sig); sleep(5); printf("wake up .....\n"); } ​ int main() { // 注册信号捕捉函数 struct sigaction act; act.sa_flags = 0; act.sa_handler = myfunc; // 设置临时屏蔽的信号 sigemptyset(&act.sa_mask); // 清空 // ctrl + 反斜杠 sigaddset(&act.sa_mask, SIGQUIT); ​ sigaction(SIGINT, &act, NULL); //注册信号 ​ while (1); ​ return 0; } ~~~ ## sigqueue函数 ~~~ #include <signal.h> ​ int sigqueue(pid_t pid, int sig, const union sigval value); 功能: 给指定进程发送信号。 参数: pid : 进程号。 sig : 信号的编号。 value : 通过信号传递的参数。 union sigval 类型如下: union sigval { int sival_int; void *sival_ptr; }; 返回值: 成功:0 失败:-1 ~~~ 向指定进程发送指定信号的同时,携带数据。但如传地址,需注意,不同进程之间虚拟地址空间各自独立,将当前进程地址传递给另一进程没有实际意义。 下面我们做这么一个例子,一个进程在发送信号,一个进程在接收信号的发送。 发送信号示例代码如下: ~~~ /******************************************************* *功能: 发 SIGINT 信号及信号携带的值给指定的进程 *参数: argv[1]:进程号 argv[2]:待发送的值(默认为100) *返回值: 0 ********************************************************/ int main() { if (argc >= 2) { pid_t pid, pid_self; union sigval tmp; ​ pid = atoi(argv[1]); // 进程号 if (argc >= 3) { tmp.sival_int = atoi(argv[2]); } else { tmp.sival_int = 100; } ​ // 给进程 pid,发送 SIGINT 信号,并把 tmp 传递过去 sigqueue(pid, SIGINT, tmp); ​ pid_self = getpid(); // 进程号 printf("pid = %d, pid_self = %d\n", pid, pid_self); } ​ return 0; } ~~~ 接收信号示例代码如下: ~~~ // 信号处理回调函数 void signal_handler(int signum, siginfo_t *info, void *ptr) { printf("signum = %d\n", signum); // 信号编号 printf("info->si_pid = %d\n", info->si_pid); // 对方的进程号 printf("info->si_sigval = %d\n", info->si_value.sival_int); // 对方传递过来的信息 } ​ int main() { struct sigaction act, oact; ​ act.sa_sigaction = signal_handler; //指定信号处理回调函数 sigemptyset(&act.sa_mask); // 阻塞集为空 act.sa_flags = SA_SIGINFO; // 指定调用 signal_handler ​ // 注册信号 SIGINT sigaction(SIGINT, &act, &oact); ​ while (1) { printf("pid is %d\n", getpid()); // 进程号 ​ pause(); // 捕获信号,此函数会阻塞 } ​ return 0; } ~~~