linux 信号操作

linux 信号操作
一个信号就是一个小消息,它通知进程系统中发生了一个某种类型的事件。
每种信号类型都对应某种系统事件。底层的硬件异常通常由内核异常处理程序来处理,用户程序对此不可见。信号提供了一种可以通知用户程序的机制,使用户知道发生了这个异常。
接下来我们来介绍下linux信号的发送、接收、处理、阻塞、等待等操作。
首先,我们来介绍信号的发送。
信号发送
使用/bin/kill发送信号
/bin/kill程序可以向其他进程发送任意指定的信号。使用
可以查看系统中的信号:
kill发送信号的语法是:
或者
当pid的值为负数时,信号会被发送到-pid进程组(关于进程组,可自行搜索相关资料)每个进程。
举例,我们启动一个进程vi(这个程序基本所有的linux发行版都会打包进去)。可以使用kill -9 vi向vi进程发送KILL信号,这时,运行vi的窗口会打印出:
(不同的终端打印出的信息不同)
vi进程收到KILL信号后被杀死。
不过,使用进程名发送信号这种方式并不推荐,因为很可能造成“误伤”,可能有多个相同的进程实例,而你可能只是想向其中的一个发送信号,这时可以通过ps命令查看要发送的进程的pid从而做到“精确投放”。
使用键盘发送信号
键盘会向当前只在运行的进程发送一些信号,比如Ctrl + C会发送INT信号,Ctrl + Z会发送TSTP信号等。
使用kill函数
kill函数和kill命令类似(只是不能接收进程名)。传入参数为进程(组)pid和信号值。
编译为mykill并运行,根据提示输入pid和信号值,程序就会给指定的进程(组)发送消息。
alarm给自己发信号
alarm函数就像一个闹钟,在指定的时间后,向自己发送一个ALRM信号。参数为一个整数,指定延迟的秒数。具体用法可以通过man alarm查看。
信号接收与处理
linux信号通过signal函数注册信号处理函数来处理。
下面来举个例子:
例子中我们为信号60,61,62,INT,TSTP设置了响应,可以看出同一个响应函数可以对应多个信号,但是注意,一个信号只能有一个响应。我们在给20((SIGTSTP)设置处理函数时使用了SIG_IGN,这个值表示忽略信号,与之类似的还有SIG_DFL,表示使用默认处理,不同的信号有不同的默认处理方式。
signal函数会返回上一个处理函数的指针,此时我们没有接收使用这个返回值。实际开发中,可能需要保存这个指针用于恢复默认行为。更多signal函数的信息,可以使用man signal命名查阅文档获得。
编译运行我们的程序,使用kill给我们的程序发信号,分别发送60,61,62,2(SIGINT),20(SIGTSTP),我们的程序会有不同的响应。然后再在终端中输入Ctrl + Z和Ctrl + C(分别对应2(SIGINT)和20(SIGTSTP)),程序会做出不同的操作。最后我们向程序发送15(SIGTERM),我们的程序会终止,因为我们并没有为15(SIGTERM)设置处理函数,而15(SIGTERM)的默认处理是终止程序运行。
需要注意的一点是:SIGKILL和SIGSTOP的默认处理是不能修改的。
还需要注意的一点是:信号的处理和主程序的逻辑是并行的,所以需要一定的同步机制,此处主程序没有过多的逻辑,所以可以忽略了这一点,实际开发中需要注意
信号阻塞与解除阻塞
信号阻塞的两种方法
隐式阻塞
信号在操作系统内核是一系列标记位,每个信号对应一个标记位。当接收到某个信号的时候,内核将该信号的标记位设置为"正在处理",等到该信号被处理完成后。当一个信号正在被处理的时候,如果再次收到该信号,那么标记位会被设置为“等待处理”,而后续到来的相同信号也只是标记“等待处理”,这就相当于没有被接收。
举个例子:
显式阻塞
还有一种方式是通过
sigprocmask函数手动阻塞某种或某几种信号。与之搭配使用的有sigemptyset,sigfillset,sigaddset,sigdelset以及一个查询函数sigismember。sigprocmask函数传入控制指令和一个信号集合,返回原信号集合(通过参数返回)。控制指令包括SIG_BLOCK(添加阻塞信号集合),SIG_UNBLOCK(删除阻塞信号集合),SIG_SETMASK(设置阻塞信号集合)。其他的配套函数对信号集合进行操作。
函数的具体说明可以参考文档。
这里我们举个例子阻塞
SIGINT和SIGTSTP信号。
等待信号
使用pause等待信号
pause会挂起当前进程,直到一个信号到来。下面演示一个例子。
编译运行程序后,程序不会退出,按下Ctrl+C发送SIGINT,然后程序打印hello world后退出。
使用sigsuspend函数等待信号
与pause不同的是,pause会等待任意信号,而sigsuspend等待指定类型的信号。
编译运行,使用kill分别发送60、SIGINT,发送60的时候程序没反应(因为已经被挂起),发送SIGINT的时候,程序恢复执行,并且之前的60信号也被执行(从这里看,sigsuspend会导致信号排队?,经验证,pause有类似表现)。
sigsuspend函数相当于将mask中标记的信号全部阻塞,然后调用pause,然后调用pause,等待到信号后,再恢复现场。
本文章简单地讨论了linux下信号的使用,但是还有很多没有提到的细节,而且例子也大多不规范(只为了说明问题),更多细节请参考官方的文档或者更权威的书籍。
版权声明: 本文为 InfoQ 作者【SkyFire】的原创文章。
原文链接:【http://xie.infoq.cn/article/3df41c1729cab1c5657782184】。文章转载请联系作者。











评论