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】。文章转载请联系作者。
评论