信号集:什么是信号集,信号集可以粗略的理解为,那个31个信号位的位图(信号集和)
我们没有声明是阻塞信号集的话指的都是未决信号集
信号集函数:man sigemptyset
清空信号集:
int sigemptyset(sigset_t *set);
填充信号集:
int sigfillset(sigset_t *set);
添加某个信号到信号集:
int sigaddset(sigset_t *set, int signum);
从集合删除某个信号:
int sigdelset(sigset_t *set, int signum);
判断信号是否为信号集里面的信号:
int sigismember(const sigset_t *set, int signum);
返回值:
前四个函数成功返回0,失败返回-1。
最后一个函数比较特殊,signum在信号集合中返回1,如果不在则返回0,要是出错返回-1.
在与不在的区别就是,在表示位图为1,不在表示位图为0
设置阻塞也可以解除阻塞信号集:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
- how
SIG_BLOCK 设置阻塞
SIG_UNBLOCK 解除阻塞
SIG_SETMASK 设置set为心得阻塞信号集 - set
传入的信号集 - oldset
旧的信号集,传出
返回值:成功返回0 失败返回-1
获取未决信号集:
int sigpending(sigset_t *set); - set:
传出参数,当前的未决信号集
返回值:成功返回0 失败返回-1
打印当前进程常规的信号集:
这里插入一个知识点:
在vim中使用for循环for(int i = 0;i < 10;i++)这种写法是c99中的写法,这样编译器会给出警告,警告是i未定义,只需要把i在for循环外定义就可以了,这个是编译器自身的问题
信号捕捉:
防止进程意外死亡(如果进程收到异常信号,我们可以把它抓住不让进程死亡)
之前已经讲过了一种捕捉信号的函数signal函数,不同的系统signal有不同的含义所以不推荐使用
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
- signum要捕捉的信号
要执行的函数指针,函数应该声明成这个样子viod func (int)void型,int型参数
证明捕捉函数的第2和3个特性
关于临时阻塞还有一个举例
运行结果:
SIGCHLD信号处理
子进程在暂停或者退出的时候会发送SIGCHLD信号,我们可以通过捕捉SIGCHLD信号来回收子进程
信号捕捉是在整个代码运行期间,只要代码不退出,就可以一直捕捉,上面的 代码过程类似于,子进程退出后,产生信号,本来在sleep的父进程就终止睡觉,去执行一下捕捉函数(因为捕捉函数捕捉的就是子进程推出的信号),然后在回来睡觉,当第二个子进程退出的时候他就会再去,这样知道子进程回收完毕,父进程就一直陷入睡觉,这样就不会产生僵尸进程,而且也不用阻塞等待,父进程可以干自己想干的事情,我感觉比直接在 父进程中用wait或者waitpid好得多
但是上面这段代码有隐患:
那倘若子进程先退出呢,父进程还没有开始执行那么子进程还是会变成僵尸进程,根据上面这段代码在创建子进程之前,先将子进程退出的信号设置为阻塞信号集,最后在父进程开始之后,在父进程注册捕捉函数之后,再将阻塞信号释放,那么就会将之前阻塞的子进程的信号全部还原然后父进程再进行统一回收,但是我要说的一点是,在我们写的第一个代码中我们的执行函数是这个样子
所以才有这样的情况,但是第二种情况是父进程还没有开始运行注册捕捉函数,所以用上面这种执行函数不能全部回收,只能回收一个,所以用第二种执行函数先将死掉的子进程全部收集起来再统一回收。