[Linux学习笔记] 浅谈信号(文章含不少学习资源)
创始人
2024-04-21 17:37:57
0

百金买骏马,千金买美人,万金买爵禄,何处买青春?

目录

信号的概念

信号的种类:

 kill -l 命令可以查看信号列表

 man 7 signal 查看信号详细内容

 信号的产生

补充知识 Core Dump(转储内存)

补充知识:与信号相关的数据结构

对于不可靠信号

 接收信号

信号的处理:

阻塞信号集:

未决信号集:

信号的捕获:

 这里为了帮助理解,展了一段存在bug的代码:

运行结果:​

结语

        参考的文章

        参考的书籍


信号的概念

        在计算机科学中,信号是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。(摘自百度百科)

        特别强调对 异步中断 理解:因为信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

信号的种类:

  kill -l 命令可以查看信号列表

 man 7 signal 查看信号详细内容

  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2
  • Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做"不可靠信号",信号值小于SIGRTMIN(在我的机器里SIGRTMIN=34,SIGRTMAX=64)的信号都是不可靠信号。这就是"不可靠信号"的来源。
  • 关于可靠信号和不可靠信号

 信号的产生

  • 按键产生,如:Ctrl+c、Ctrl+z、Ctrl+\
  • 系统调用产生,如:kill()、raise()、abort()
  • 软件条件产生,如:定时器alarm()、pause()
  • 硬件异常产生,如:非法访问内存(段错误)、除0(浮点数例外)、内存对齐出错(总线错误)
  • 命令产生,如:kill命令

上面的一些函数或命令都对百度百科进行了超链接,如果还有疑问,可以参考 《unix 环境高级编程[第10章]》

补充知识 Core Dump(转储内存)

  • wait()和waitpid(),都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。 status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图:

        

  • 关于wait()和waitpid()所返回相关的宏
  •  linux中core dump开启使用教程

我们可以通过位运算,也可以通过宏来得到我们需要的信息,具体代码如下

#include 
#include 
#include 
#include 
#include 
#include int main(void)
{pid_t pid;if ((pid = fork()) == -1)perror("fork"), exit(1);if (pid == 0)  //child{sleep(4);int a=13/0; //故意写错,让程序异常退出exit(0xFF);}else{int st;int ret = wait(&st);// if(ret > 0 &&WIFEXITED(st) )  WIFEXITED(st)进程正常退出为真,和下面等价if (ret > 0 && (st & 0X7F) == 0){ // 正常退出printf("正常退出\n");printf("宏:st:%p child exit code:%d sig code : %d     \n",st,WEXITSTATUS(st),WTERMSIG(st));printf("移位运算:st:%p child exit code:%d sig code : %d\n",st,(st >> 8) & 0XFF,st & 0X7F);printf("core文件是否产生:%d,core dump 标志是:%d\n",WCOREDUMP(st),st & 0X80);}else if (ret > 0){ // 异常退出printf("异常常退出\n");printf("宏:st:%p child exit code:%d sig code : %d     \n",st,WEXITSTATUS(st),WTERMSIG(st));printf("移位运算:st:%p child exit code:%d sig code : %d\n",st,(st >> 8) & 0XFF,st & 0X7F);printf("core文件是否产生:%d,core dump 标志是:%d\n",WCOREDUMP(st),st & 0X80);//core会输出128,因为它刚好在第8位 ,即0X80}}
}

 运行结果:

补充知识:与信号相关的数据结构

具体一点是这样子(图片来源)

 然后我们再画简单点

对于不可靠信号

  •  每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。 如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。

 接收信号

信号的处理:

  1. 执行默认动作
  2. 忽略(丢弃)
  3. 捕捉(调用户处理函数)

阻塞信号集:

  • 将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(解除屏蔽后)。

未决信号集:

  1. 信号产生,未决信号集中描述该信号的位立刻翻转为1,表信号处于未决状态。当信号被处理对应位翻转回为0。这一时刻往往非常短暂。
  2. 信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态。

介绍了这么多知识,现在我们来对信号再进行一个捕获

信号的捕获:

  • 如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码 是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。当前正在执行 main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号 SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler 和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。 sighandler函数返 回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复 main函数的上下文继续执行了。

 这里为了帮助理解,展了一段存在bug的代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include void handler1(int sig){int olderrno=errno;pid_t child=waitpid(-1,NULL,0);if(child<0)fprintf(stderr,"    waitpid error");printf("    Handler reaped child %d\n",(int)child);sleep(1);printf("%s\n",strerror(olderrno));  
}int main(){/*第一个参数 是信号名       SIGHLD 在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程,按系统默认将忽略此信号,如果父进程希望被告知其子系统的这种状态,则应捕捉此信号。第二参数是信号处理函数  其中 常量SIG_IGN代表忽略 SIG_DFL代表 按系统默认动作 */  //返回值 若成功,返回以前的信号处理配置; 若出错,返回 SIG_ERRif(signal(SIGCHLD,handler1)==SIG_ERR)   //对信号进行捕获fprintf(stderr,"signal error");//创建子进程int i=0;for(i=0;i<3;i++){if(fork()==0){ //childprintf("Hello i am child %d\n",(int)getpid());exit(0);}}//父进程运行printf("Hi i am parent %d\n",(int)getpid());while(1);exit(0);
}

运行结果:​​​​​​​

 这里我们注意到尽管发送了3个SIGCHLD信号给父进程,但其中只有两个信号被成功接收了,因此父进程只回收了两个子进程.而子进程31288则成为了一个僵尸进程.

那么是哪里出错了呢?这段代码来源《深入理解计算机系统(第8章 图8-36)》

结语

本文也仅仅是对信号的一个浅谈,意在先树立一个大的框架,希望对读者能有所帮助,作者本人也在一路学习的过程中,很多细节由于自身经验和时间的原因,并没办法给读者细细道来,但是本文所参考的文章和书籍均会在文末放上链接,当然我也很欢迎读者如果有问题可以私信于我或在底下评论,我也会很乐意分享我的见解.

这些是我们没有细细讨论的内容,其实每一个函数都很有意思,这边再次推荐一下这本好书《UNIX环境高级编程》,当然还有《深入理解计算机系统》

 参考的文章

Linux应用与网络编程 - 随笔分类(第2页) - 浅墨浓香

Linux - 信号 | C++ 全栈知识体系

可靠信号与不可靠信号 - wsw_seu - 博客园

linux 信号处理 六(全) - 走看看

参考的书籍

 《深入理解计算机系统》

 《UNIX环境高级编程》

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...