首页 > 代码库 > linux系统编程之信号(四)
linux系统编程之信号(四)
今天继续探讨信号相关的东东,话不多说,正入正题:
信号在内核中的表示:
下面用图来进一步描述这种信号从产生到递达之间的状态(信号阻塞与未诀):
那是怎么来决定的呢?下面慢慢来举例分解:
所以,通过这些图,可以描述信号从产生到递达的一个过程,上面的理解起来可能有点难,下面会用代码来进一步阐述,在进行实验之前,还需了解一些函数的使用,这些函数在实验中都会被用到,也就是信号集操作函数。
信号集操作函数:
其中解释一下sigset_t,百度百科解释为:
而这个函数的意义就是将这64位清0
这个函数的意义是将这屏蔽字的64位都变为1
将这个信号所对应的位置为1
将这个信号所对应的位置为0
检测这一个信号所对应的位当前是0还是1
以上是操作信号集的五个相关的函数,但是注意:这五个函数仅仅是改变这个信号集变量,如set,并非真正改变进程信号当中的屏蔽字,所以接下来介绍的函数就是改变信号当中的屏蔽字的
sigprocmask:
好了,理论说了很多,下面正式开始实验,来体会一下一个信号从产生到递达的一个状态转换过程:
首先,我们打印出系统的未诀信号,目的是为了观察之后的状态:
#include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0) void handler(int sig); void printsigset(sigset_t *set)//打印出信号集的状态,其中参数set为未诀状态的信号集 { int i; for (i=1; i<NSIG; ++i)//NSIG表示信号的最大值,也就是等于64 { if (sigismember(set, i))//说明是未诀状态的信号 putchar(‘1‘); else putchar(‘0‘);//说明不是未诀状态的信号 } printf("\n"); } int main(int argc, char *argv[]) { sigset_t pset; for (;;) { sigpending(&pset);//该函数是获取进程当中未诀状态的信号集 ,保存在pset当中 printsigset(&pset);//打印信号集的状态,看有没有未诀状态的信号产生 sleep(1); } return 0; }
【说明】:sigpending是用来获取进程中所有的未诀信号集:
这时看一下运行效果:
可以发现,当前状态没有未诀的信号,因为还没有被阻塞的信号过,信号也没有产生过,所以不可能有未诀的状态。
这时,我们来安装一个SIGINT信号:
#include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0) void handler(int sig); void printsigset(sigset_t *set) { int i; for (i=1; i<NSIG; ++i) { if (sigismember(set, i)) putchar(‘1‘); else putchar(‘0‘); } printf("\n"); } int main(int argc, char *argv[]) { sigset_t pset; if (signal(SIGINT, handler) == SIG_ERR)//安装一个SIGINT信号 ERR_EXIT("signal error"); for (;;) { sigpending(&pset); printsigset(&pset); sleep(1); } return 0; } void handler(int sig) { printf("recv a sig=%d\n", sig); }
这时再看下效果:
从结果来看,信号被直接递达了,所以这次也没有看到有1的未诀状态的信号,因为信号必须被阻塞才会出现未诀状态,所以接下来将SIGINT信号利用上面介绍到的函数来将其阻塞掉:
#include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0) void handler(int sig); void printsigset(sigset_t *set) { int i; for (i=1; i<NSIG; ++i) { if (sigismember(set, i)) putchar(‘1‘); else putchar(‘0‘); } printf("\n"); } int main(int argc, char *argv[]) { sigset_t pset; sigset_t bset; sigemptyset(&bset);//将信号集清0 sigaddset(&bset, SIGINT);//将SIGINT所对应的位置1 if (signal(SIGINT, handler) == SIG_ERR) ERR_EXIT("signal error"); sigprocmask(SIG_BLOCK, &bset, NULL);//更改进程中的信号屏蔽字,其中第三个参数传NULL,因为不关心它原来的信号屏蔽字 for (;;) { sigpending(&pset); printsigset(&pset); sleep(1); } return 0; } void handler(int sig) { printf("recv a sig=%d\n", sig); }
编译运行:
从结果来看,将SIGINT信号来了,由于添加到了信号屏蔽字为1,所以会被阻塞掉,并且可以看到SIGINT对应的位也打印为1了。
【说明】:SIGINT对应的位是指:
下面,我们做一件事情,就是当我们按下ctrl+\解除阻塞,这样处于未诀状态的信号就会被递达,则对应的未诀状态位也会还原成0,具体代码如下:
#include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0) void handler(int sig); void printsigset(sigset_t *set) { int i; for (i=1; i<NSIG; ++i) { if (sigismember(set, i)) putchar(‘1‘); else putchar(‘0‘); } printf("\n"); } int main(int argc, char *argv[]) { sigset_t pset; sigset_t bset; sigemptyset(&bset); sigaddset(&bset, SIGINT); if (signal(SIGINT, handler) == SIG_ERR) ERR_EXIT("signal error"); if (signal(SIGQUIT, handler) == SIG_ERR)//注册一个ctrl+c信号 ERR_EXIT("signal error"); sigprocmask(SIG_BLOCK, &bset, NULL); for (;;) { sigpending(&pset); printsigset(&pset); sleep(1); } return 0; } void handler(int sig) { if (sig == SIGINT) printf("recv a sig=%d\n", sig); else if (sig == SIGQUIT) { sigset_t uset;//当按下ctrl+\时,则对SIGINT信号解除阻塞 sigemptyset(&uset); sigaddset(&uset, SIGINT); sigprocmask(SIG_UNBLOCK, &uset, NULL); } }
编译运行:
从中可以看到,当我们按下ctrl+\时,并没有退出,而是解除了阻塞,所以对应的SIGINT位也变为0了。
另外,看下这种情况:
多次按了ctrl+c,可在按ctrl+\解除阻塞时,只响应了一次信号处理函数,这也由于SIGINT是不可靠信号,不支持排队。
另外,由于我们捕获了ctrl+\信号,所以没办法退出这个进程了,那怎么办呢,可以利用shell命令将其强制杀掉如下:
好了,今天学的东西可能有些生涩,需好好消化,下节再见!
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。