1. 什么是信号?
网上给出的定义是:在计算机科学中,信号(英语:Signals)是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。
简单的理解信号就是一种Linux环境下进程通讯的一种机制。
2. 信号怎么产生的?
信号一般是有以下3个方面产生:
通过终端按键产生信号
调用系统函数产生信号
软件产生的信号
1>终端按键就是我们在Linux中写程序是想要终止某个程序,就会按ctrl+c按键,还有类似ctrl+d、ctrl+\、ctrl+z等。
2>调用系统函数可以产生信号,比如
这里我们将一个死循环的进程放在后台,而我们可以使用一个kill 命令向该进程发送信号。
int kill(pid_t pid, int sig); int raise(int sig); //返回值:成功为0,失败为-1;
在上面的图上我们可以看到一个进程可以调用系统函数来发送信号。
3>有软件产生的信号,我们在管道的时候,有一个管道产生的信号SIGPIPE,这个就是软件信号。
还有我们的alarm函数,相当于定一个闹钟,时间到了会发送一个SIGALRM信号。
unsigned int alarm(unsigned int seconds); //返回值:0或者是当前还剩余的秒数。
如图:
扩展:Core Dump(核心转储)
定义为:核心转储(core dump),在汉语中有时戏称为吐核,是操作系统在进程收到某些信号而终止运行时,将此时进程地址空间的内容以及有关进程状态的其他信息写出的一个磁盘文件。这种信息往往用于调试。
当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做Core Dump(中文有的翻译成“核心转储”)。我们可以认为 core dump 是“内存快照”,但实际上,除了内存信息之外,还有些关键的程序运行状态也会同时 dump 下来,例如寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。core dump 对于编程人员诊断和调试程序是非常有帮助的,因为对于有些程序错误是很难重现的,例如指针异常,而 core dump 文件可以再现程序出错时的情景。
而Linux操作系统默认不会产生core文件的,因为core文件有可能会包括用户的信息,不安全。而我们如果是在测试间断可以使用ulimie命令改变这个命令,使其产生core文件
ulimit -c 1024//创建1024大小的core文件,你以后生成的core大小就是这么大。
3. 信号有哪些?
在命令行中输入kill -l就可看到信号了:
我们可以看到共有1-31 34-64共62个信号,其中1-31是普通信号,34-64为实时信号。
4. 信号的阻塞
信号相关的其他概念
实际执行信号的动作叫“递达”
信号从产生到递达之间叫“未决”
信号可以被阻塞起来
这个流程示意图如下:信号在内核中的示意图
sigset_t信号集
typedef struct { unsigned long sig[_NSIG_WORDS]; } sigset_t
我们从上面的图可以看到阻塞,未决都是由0 1 来控制他们是否被响应。
信号集操作函数
int sigemptyset(sigset_t *set);//初始化set指向的信号集,使所有的bit为清0, //表示该信号集不包含任何有效信号。 int sigfillset(sigset_t *set);//初始化set指向的信号集,使所有的bit置位 //表示该信号集的有效信号包含所有信号。 int sigaddset(sigset_t *set, int signum);//修改pending表 int sigdelset(sigset_t *set, int signum); int sigismember(const sigset_t *set, int signum);//得到信号集 //返回值,前四个函数成功为0。失败为-1; //sigismember是一个布尔函数,用来判断某个信号集中是否包含某个信号, //包含为1,不包含为0,出错为-1;
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); //这个函数可以读取或者更改进程的信号屏蔽字(阻塞信号集) //返回值:成功为0,失败为-1;
int sigpending(sigset_t *set); //读取该进程的未决信号集,通过set参数传出 //返回值:成功为0;失败为-1;
通过上述我们可以试着写个程序验证一下:
#include <stdio.h> #include <unistd.h> #include <signal.h> void printsigset(sigset_t* set) { int i = 0; for(;i<32;i++) { if(sigismember(set,i))//判断指定信号是否在目标集合中 { putchar('1'); } else { putchar('0'); } } puts(""); } int main() { sigset_t i,j; sigemptyset(&i);//定义信号集对象,并且清空初始化 sigaddset(&i,SIGINT);//将ctrl+c定义成未决 sigprocmask(SIG_BLOCK,&i,NULL);//将ctrl+c阻塞 while(1) { sigpending(&j);//获取未决信号集 printsigset(&j); sleep(1); } return 0; }
效果图:
信号的捕捉