UNIX环境高级编程——信号
一、信号生命周期
从信号发送到信号处理函数的执行完毕。
对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:信号诞生;信号在进程中注册完毕;信号在进程中的注销完毕;信号处理函数执行完毕。相邻两个事件的时间间隔构成信号生命周期的一个阶段。
下面阐述四个事件的实际意义:
1. 信号"诞生"。信号的诞生指的是触发信号的事件发生(如检测到硬件异常、定时器超时以及调用信号发送函数kill()或sigqueue()等)。
2. 信号在目标进程中"注册";进程的task_struct结构中有关于本进程中未决信号的数据成员:
struct sigpending pending:
struct sigpending{
struct sigqueue *head, **tail;
sigset_t signal;
};
第三个成员是进程中所有未决信号集,第一、第二个成员分别指向一个sigqueue类型的结构链(称之为"未决信号信息链")的首尾,信息链中的每个sigqueue结构刻画一个特定信号所携带的信息,并指向下一个sigqueue结构:
struct sigqueue{
struct sigqueue *next;
siginfo_t info;
}
信号在进程中注册指的就是信号值加入到进程的未决信号集中(sigpending结构的第二个成员sigset_t signal),并且信号所携带的信息被保留到未决信号信息链的某个sigqueue结构中。 只要信号在进程的未决信号集中,表明进程已经知道这些信号的存在,但还没来得及处理,或者该信号被进程阻塞。
注:
当一个实时信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此,信号不会丢失,因此,实时信号又叫做"可靠信号"。这意味着同一个实时信号可以在同一个进程的未决信号信息链中占有多个sigqueue结构(进程每收到一个实时信号,都会为它分配一个结构来登记该信号信息,并把该结构添加在未决信号链尾,即所有诞生的实时信号都会在目标进程中注册);
当一个非实时信号发送给一个进程时,如果该信号已经在进程中注册,则该信号将被丢弃,造成信号丢失。因此,非实时信号又叫做"不可靠信号"。这意味着同一个非实时信号在进程的未决信号信息链中,至多占有一个sigqueue结构(一个非实时信号诞生后,(1)如果发现相同的信号已经在目标结构中注册,则不再注册,对于进程来说,相当于不知道本次信号发生,信号丢失;(2)如果进程的未决信号中没有相同信号,则在进程中注册自己)。
3. 信号在进程中的注销。在目标进程执行过程中,会检测是否有信号等待处理(每次从系统空间返回到用户空间时都做这样的检查)。如果存在未决信号等待处理且该信号没有被进程阻塞,则在运行相应的信号处理函数前,进程会把信号在未决信号链中占有的结构卸掉。是否将信号从进程未决信号集中删除对于实时与非实时信号是不同的。对于非实时信号来说,由于在未决信号信息链中最多只占用一个sigqueue结构,因此该结构被释放后,应该把信号在进程未决信号集中删除(信号注销完毕);而对于实时信号来说,可能在未决信号信息链中占用多个sigqueue结构,因此应该针对占用sigqueue结构的数目区别对待:如果只占用一个sigqueue结构(进程只收到该信号一次),则应该把信号在进程的未决信号集中删除(信号注销完毕)。否则,不应该在进程的未决信号集中删除该信号(信号注销完毕)。
进程在执行信号相应处理函数之前,首先要把信号在进程中注销。
4. 信号生命终止。进程注销信号后,立即执行相应的信号处理函数,执行完毕后,信号的本次发送对进程的影响彻底结束。
注:
1)信号注册与否,与发送信号的函数(如kill()或sigqueue()等)以及信号安装函数(signal()及sigaction())无关,只与信号值有关(信号值小于SIGRTMIN的信号最多只注册一次,信号值在SIGRTMIN及SIGRTMAX之间的信号,只要被进程接收到就被注册)。
2)在信号被注销到相应的信号处理函数执行完毕这段时间内,如果进程又收到同一信号多次,则对实时信号来说,每一次都会在进程中注册;而对于非实时信号来说,无论收到多少次信号,都会视为只收到一个信号,只在进程中注册一次。
二、信号编程注意事项
1. 防止不该丢失的信号丢失。如果对文中所提到的信号生命周期理解深刻的话,很容易知道信号会不会丢失,以及在哪里丢失。
2. 程序的可移植性
考虑到程序的可移植性,应该尽量采用POSIX信号函数,POSIX信号函数主要分为两类:
POSIX 1003.1信号函数: Kill()、sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、sigpending()、sigprocmask()、sigsuspend()。
POSIX 1003.1b信号函数。POSIX 1003.1b在信号的实时性方面对POSIX 1003.1做了扩展,包括以下三个函数: sigqueue()、sigtimedwait()、sigwaitinfo()。 其中,sigqueue主要针对信号发送,而sigtimedwait及sigwaitinfo()主要用于取代sigsuspend()函数,后面有相应实例。
#include <signal.h>
int sigwaitinfo(sigset_t *set, siginfo_t *info);
该函数与sigsuspend()类似,阻塞一个进程直到特定信号发生,但信号到来时不执行信号处理函数,而是返回信号值。因此为了避免执行相应的信号处理函数,必须在调用该函数前,使进程屏蔽掉set指向的信号,因此调用该函数的典型代码是:
sigset_t newmask;
int rcvd_sig;
siginfo_t info;
sigemptyset(&newmask);
sigaddset(&newmask, SIGRTMIN);
sigprocmask(SIG_BLOCK, &newmask, NULL);
rcvd_sig = sigwaitinfo(&newmask, &info)
if (rcvd_sig == -1) {
..
}
调用成功返回信号值,否则返回-1。sigtimedwait()功能相似,只不过增加了一个进程等待的时间。
3. 程序的稳定性
为了增强程序的稳定性,在信号处理函数中应使用可重入函数。
信号处理程序中应当使用可再入(可重入)函数(注:所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错)。因为进程在收到信号后,就将跳转到信号处理函数去接着执行。如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,可能会出现不可预料的后果。不可再入函数在信号处理函数中被视为不安全函数。
满足下列条件的函数多数是不可重入的:
(1)使用静态的数据结构,如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等;
(2)函数实现时,调用了malloc()或者free()函数;
(3)实现时使用了标准I/O函数的。
The Open Group视下列函数为可再入的:
_exit()、access()、alarm()、cfgetispeed()、cfgetospeed()、cfsetispeed()、cfsetospeed()、chdir()、chmod()、chown() 、close()、creat()、dup()、dup2()、execle()、execve()、fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、 geteuid()、getgid()、getgroups()、getpgrp()、getpid()、getppid()、getuid()、kill()、link()、lseek()、mkdir()、mkfifo()、 open()、pathconf()、pause()、pipe()、raise()、read()、rename()、rmdir()、setgid()、setpgid()、setsid()、setuid()、 sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、signal()、sigpending()、sigprocmask()、sigsuspend()、sleep()、stat()、sysconf()、tcdrain()、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、 umask()、uname()、unlink()、utime()、wait()、waitpid()、write()。
即使信号处理函数使用的都是"安全函数",同样要注意进入处理函数时,首先要保存errno的值,结束时,再恢复原值。因为,信号处理过程中,errno值随时可能被改变。另外,longjmp()以及siglongjmp()没有被列为可再入函数,因为不能保证紧接着两个函数的其它调用是安全的。
UNIX环境高级编程——信号的更多相关文章
- UNIX环境高级编程——信号说明列表
$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGK ...
- Unix环境高级编程---信号
参考博客:http://blog.csdn.net/alex_my/article/details/39494129 1. 信号概念 何为信号? 信号是一种软中断,可以由以下情形触发: -1: 用户按 ...
- UNIX环境高级编程——信号(API)
一.信号在内核中的表示 实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending).进程可以选择阻塞(Block)某个信号.被阻塞的信号 ...
- UNIX环境高级编程——信号之kill、raise、killpg、alarm、pause、abort、sleep、usleep、nanosleep和setitimer函数
一.kill, raise, killpg 函数 int kill(pid_t pid, int sig); int raise(int sig); int killpg(int pgrp, int ...
- UNIX环境高级编程——信号基本概述和signal函数
一.为了理解信号,先从我们最熟悉的场景说起:1. 用户输入命令,在Shell下启动一个前台进程.2. 用户按下Ctrl-C,这个键盘输入产生一个硬件中断.3. 如果CPU当前正在执行这个进程的代码,则 ...
- UNIX环境高级编程——sigqueue、sigsuspend函数
一.sigqueue函数 功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用. int sigqueue(pid_t pid, int sig, ...
- (八) 一起学 Unix 环境高级编程 (APUE) 之 信号
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
随机推荐
- linux系统下安装jdk、tomcat、mysql、redis
一,安装jdk 1.rpm -qa | grep jdk 检测是否安装jdk 2.rpm -e --nodeps 要卸载的已有jdk名称 3.tar -zxvf jdk-7u71-linux-i586 ...
- JS运行机制之 Event Loop 的思考
先举个栗子,如下: for (var i = 0; i < 5; i++) { setTimeout(function() { console.log('i: ',i); //一秒之后输出几乎没 ...
- H5canvas基础
本篇文章开始讲解HTML5的核心功能之一:Canvas 通过Canvas可以动态生成和展示图形.图表.图像以及动画. Canvas API功能非常多,我们将讨论最常用的功能. 我们先新建一个canva ...
- 阿里架构师带你深入浅出jvm
本文跟大家聊聊JVM的内部结构,从组件中的多线程处理,JVM系统线程,局部变量数组等方面进行解析 JVM JVM = 类加载器(classloader) + 执行引擎(execution engine ...
- JavaScript正则表达式模式匹配(1)——基本字符匹配
var pattern=/g..gle/; //点符号表示匹配除了换行符外的任意字符 var str='g78gle'; alert(pattern.test(str)); var pattern=/ ...
- 小程序上拉下拉共存时不可使用scroll-view的解决方法
使用 bindscrolltolower ,必须搭配使用的 scroll-view 会导致小程序 "enablePullDownRefresh": true 下拉不能使用. 解决方 ...
- ES6(数据结构_2)
数据结构—2 Map 与 Array 的对比 Set 与 Array的对比 Map 与 Object 的对比 Set 与 Object 的对比 (增.查.改.删) 一.Map 与 Array 的对比 ...
- 什么是 Docker
Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目.它基于 Google 公司推出的 Go 语言实现. 项目后来加入了 Linux 基金会,遵从了 ...
- Matlab—regexp正则表达式
原文转自:http://blog.csdn.net/yf210yf/article/details/42421523 关于正则表达式的基本知识 正则表达式就是一个表达式(也是一串字符),它定义了某种字 ...
- 如何处理IO
Network I/O operations in user code should only be done through the Nginx Lua API calls as the Nginx ...