01
信号在内核中的表示
实际执行信号的处理动作称为信号递达(Delivery);
信号从产生到递达之间的状态,称为信号未决(Pending);
进程可以选择阻塞(Block)某个信号,SIGKILL 和 SIGSTOP 不能被阻塞;
被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
每个信号都有两个标志位分别表示阻塞和未决,还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。
信号未阻塞也未产生过,当它递达时执行默认处理动作。
信号已经产生,但正在被阻塞,所以暂时不能递达。即使它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
信号虽然还没有产生,但是已经被设置为阻塞状态,一旦产生将被阻塞。
int sigemptyset(sigset_t *set);
把信号添加到信号集:
int sigaddset(sigset_t *set, int signum);
把信号集更新到进程控制块中:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how参数含义:
SIG_BLOCK:把信号集中的信号设置为阻塞状态;
SIG_UNBLOCK:把信号集中的信号设置为非阻塞状态;
SIG_SETMASK:信号状态设置为 set 指向的值。
int sigpending(sigset_t *set);
判断信号是不是未决状态:
int sigismember(const sigset_t *set, int signum);
03
阻塞和忽略
阻塞示例代码:
#include #include #include #include #include #include #include #include void printsigset(const sigset_t *p_set){ int i = 0; //遍历64个信号, for (; i < 64; i++) { //信号从1开始 判断哪些信号在信号未决状态字中 if (sigismember(p_set, i + 1)) putchar('1'); else putchar('0'); } printf("\n");}void catch_signal(int sign){ switch (sign) { case SIGINT: printf("accept SIGINT!\n"); exit(0); break; case SIGQUIT: printf("accept SIGQUIT!\n"); //收到SIGQUIT,取消SIGINT的阻塞状态 sigset_t uset; sigemptyset(&uset); sigaddset(&uset,SIGINT); //将信号集uset更新到进程控制块PCB结构中,取消阻塞信号SIGINT sigprocmask(SIG_UNBLOCK,&uset,NULL); break; }}int main(){ //定义未决信号集(pending)、阻塞信号集 sigset_t p_set, b_set; //清空信号集 sigemptyset(&b_set); //将信号SIGINT加入到信号集中 sigaddset(&b_set, SIGINT); //注册信号 if (signal(SIGINT, catch_signal) == SIG_ERR) { perror("signal error"); return -1; } if (signal(SIGQUIT, catch_signal) == SIG_ERR) { perror("signal error"); return -1; } //将信号集b_set更新到进程控制块PCB结构中,阻塞信号SIGINT(即使用户按下ctrl+c,信号也不会递达) sigprocmask(SIG_BLOCK, &b_set, NULL); while (1) { /* * 获取当前信号未决信息,即使在sigprocmask()函数中设置了信号阻塞, * 但是如果没有信号的到来,信号未决状态字对应位依然是0 * 只要有信号到来,并且被阻塞了,信号未决状态字对应位才会是1 * */ sigpending(&p_set); //打印信号未决信息 printsigset(&p_set); sleep(1); } return 0;}
运行结果如下:
虽然 SIGINT 被设置为阻塞,但是进程还没有收到 SIGINT 信号,所以不存在未决信号。
按下 CTRL + C,结果如下:
进程收到 SIGINT 信号,由于 SIGINT 被设置为阻塞状态,所以不能递达,成了未决状态。
按下 CTRL + \,即发送 SIGQUIT 信号:
进程收到 SIGQUIT 信号,执行 catch_signal 信号处理函数,取消了 SIGINT 的阻塞属性。由于 SIGINT 处于未决状态,被取消阻塞后,继续执行信号处理函数,退出进程。 信号阻塞可以理解成,发出了信号,但是拦住他,此时就处于未决状态;松开他,信号还会继续发送抵达进程。 忽略示例代码:#include int main(){ signal(SIGINT, SIG_IGN); while (1); return 0;}
这个就很好理解了,运行的时候,无论怎么按 CTRL + C,代码都不会停止。
信号阻塞和信号忽略的区别:阻塞是发出了信号,但是信号没有递达,所以进程没有响应;忽略是发出了信号,进程收到了信号,采取的动作是忽略。
PS.春招已经如此艰难,备战秋招,刻不容缓。智能硬件VIP培训班,3个月教学+1个月项目实战。有需要得同学扫码咨询!