一. 信号的捕捉定义
用户提供一个处理函数, 要求内核在处理信号时必须切换到用户态,执行这个函数, 这种方式就叫做信号的捕捉
二. 图解信号的捕捉过程
1. 由上图可以看出,当处理信号的执行动作时用户自定义的时候,此时就需返回该函数去调用该函数, 这就叫做信号的捕捉. 以前我们总是说进程收到信号时会在合适的时候取处理该信号, 现在可以看出,这个合适的时候就是当进程处理完中断时, 需需要从内核态返回到用户态的时候就需要处理能够递达的信号.举个例子, 用户注册了一个信号, 当系统进程正在执行主控制流的时候, 发现有异常中断产生, 此时系统从用户态转到内核态, 处理完异常中断时, 需要返回到main函数时,检测是否有可以递达的信号,发现有一个可以递达的信号, 而该信号的处理动作是用户自定义类型(signalhandle), 于是系统就从内核态切换到用户态去执行该信号的执行动作(signalhandle), 当处理完这个函数的时候, 系统不能直接返回到主控制流, 而是先通过 sigreturn 切换到内核状态, 此时再继续检测是否有可以递达的信号, 发现没有, 于是系统切换到用户态, 返回到刚刚被切换的主控制流处继续执行下面的代码.(注意main函数和signalhandle 之间使用不同的堆栈结构,它们之间不存在调用和被调用的关系, 是两个独立的控制流)
三. 相关接口函数
1. 捕捉信号的相关接口
;
该函数是用来读取和修改相关对信号操作的处理动作. 其中signum 是信号的编号, act 和 oact 是一个指向结构体 sigaction 的指针, act 用来存放需要修改的函数处理动作, oldact 用来保存原有的处理信号的动作, 是一个输出型参数, 可以传出原有的信号处理的动作. signal 函数中 signum 指的是信号的编号, handle 赋值为 SIGIGN 时表示忽略该信号, 当赋值为 SIG_DFL 时表示执行默认动作. 也可以赋值为函数指针, 表示执行用户自定义函数, 即捕捉信号.该函数同样不是被 main函数调用, 而是被系统调用.
2. 挂起进程的相关接口
该函数用于将调用进程挂起直到有信号递达. 如果信号的执行动作是终止信号, 则进程终止,pause 不返回 如果该信号的执行动作是忽略, 则进程继续处于挂起, pause 不返回.如果信号的执行动作是捕捉, 则pause 返回 -1. 即pause 只有出错时才有返回值
四. SIGCHLD信号
该信号用于子进程退出时操作系统向父进程发送一个SIGCHLD信号,通知父进程回收子进程, 试想, 当我们将SIGCHLD信号捕捉, 然后将该信号的处理动作设为 SIG_IGN, 忽略该信号, 此时父进程将永远不可能回收子进程, 即子进程也不会变成僵尸进程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>void handler(int sig)
{pid_t pid;while( (pid = waitpid(-1, NULL, WNOHANG)) > 0 ){printf("wait child is sucessful\n");}printf("child is quit\n");
}
int main()
{pid_t pid;signal(SIGCHLD, handler);pid = fork();if(pid == 0){printf("I am child\n");exit(1);}else if(pid > 0){while(1){printf("father is doing somthing\n");sleep(1);}}return 0;
}
此时可以看见给父进程发送一个SIGCHLD 信号,之后, 子进程也会被回收