信号的产生
1、键盘产生的信号,只能发送到前台。(如:ctrl+c、ctrl+z、ctrl+\)
2、软硬件的异常。(指针异常、除0等)
3、进程调用kill 命令或kill函数。
在Linux下查看信号 kill -l
可以查看共有62个信号。1-31为普通信号,34-64为实时信号
一个进程是或否收到1-31号的信息?用什么数据结构比较好?
答:位图(32个bit位)。其中比特位的位置是信号的编号,内容1收到0没收到
如上图./mysig后就是在前台运行,输入shell命令不能执行,在前台运行的进程可以用Ctrl+c终止进程就相当于给进程发了2号信号。
如上图./mysig后就是在后台运行,输入shell命令可以执行,在后台运行的进程用Ctrl+c不能杀死,可以发9号终止进程。如下:
其中前台和后台还有区别:
有+代表在前台运行,没有+代表在后台运行。
信号的处理
1、忽略
2、默认动作
3、自定义捕捉
默认动作就是接收到信号知道怎么处理,比如指针异常实际就是操作系统检测到异常发给了进程11号信号,除0就是发给了进程8号信号,在进程收到这些信号就终止程序。
自定义捕捉函数:
typedef void (*sighandler_t)(int)
sihhandler signal(int signum,sighandler_t handler)
信号阻塞
实际执行信号的处理动作称为信号递达。
信号从产生到递达之间的状态称为未决。
信号被阻塞将不能进行递达,除非解除阻塞。
阻塞与忽略是不相同的,阻塞是在递达之前,忽略是在递达之后。
信号在内核中的示意图:
其中block和pending都是位图,位图的位置都是信号的编号,block中内容1表示阻塞,0表示未阻塞,pending表中内容1表示收到信号,0表示未收到信号。handler表是函数指针数组。SIG_DFL是默认处理SIG_IGN是忽略,void signal是自定义捕捉。
所以操作系统发信号给进程就是操作系统修改了进程PCB中的pending表对应的bit位。
信号屏蔽
信号集操作函数
sigset_t类型对于每种信号用⼀一个bit表示“有效”或“无效”状态
int sigemptyset(sigset_t *set);//初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号
int sigfillset(sigset_t *set);//t初始化set所指向的信号集,使其中所有信号的对应bit置位有效,表示该信号集的有效信号包括系统支持的所有信号
int sigaddset(sigset_t *set, int signo);//信号集中添加某种有效信号
int sigdelset(sigset_t *set, int signo);//信号集中删除某种有效信号
int sigismember(const sigset_t *set, int signo); //用于判断一个信号集的有效信号中是否包含某种信号
调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1
how参数:1)SIG_BLOCK添加到当前信号屏蔽字的信号
2)SIG_UNBLOCK从当前信号屏蔽字中解除的信号
3)SIG_SETMASK设置当前信号屏蔽字为set所指向的值
spending()获取pending表
信号屏蔽代码:
上面第一张图片是打印
第二张图中就是进行屏蔽2号信号。
结果:
(在没发2号信号pending表是全零,发了以后第二个位置变为1,其他都为0,2号信号被屏蔽,所以不能进行递达就有了第二个位置一直为1)
信号屏蔽与解除并递达:
上面第一张图片是打印
第二张图中就是进行屏蔽2号信号,并在count++到10后解除并递达,。
结果: