[1]概述
1.每个线程都拥有独立的阻塞信号掩码。
2.开会时关闭手机是一种比较极端的例子。更合理的做法是暂时屏蔽部分人的电话。对于某些重要的电话,比如儿子老师的电话、父母的电话或老板的电话,是不希望被屏蔽的。信号也是如此。进程在执行某些操作的时候,可能只需要屏蔽一部分信号,而不是所有信号。
3.信号集: 数据类型为 sigset_t,sigset_t 的类型是位掩码,每一个比特代表一个信号。
4.SIGKILL 信号和 SIGSTOP 信号不能被阻塞。(设置信号集时会被内核剔除)(避免出现神仙进程)
5.对于多线程的进程而言,每一个线程都有自己的阻塞信号集。
[2]常用API
int sigemptyset(sigset_t *set); // 初始化信号集set中的信号为空
int sigfillset(sigset_t *set); // 将所有信号添加进信号集set
int sigaddset(sigset_t *set, int signum); // 在信号集中添加signum信号
int sigdelset(sigset_t *set, int signum); // 在信号集中删除signum信号
int sigismember(const sigset_t *set, int signum); // 判断signum是否存在于set信号集中
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); // 设置信号集
oldset: 如果oldset为非NULL,则信号掩码的先前值存储在oldset中,故一般设置为NULL。
how的选项:
SIG_BLOCK: 在当前阻塞信号集中增加set信号集中的信号
SIG_UNBLOCK:在当前阻塞信号集中删除set信号集中的信号
SIG_SETMASK:阻塞信号集被设置为set信号集。
[3]pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
1.为了更显式地设置线程的阻塞信号掩码,线程库提供了 pthread_sigmask 函数来设置线程的阻塞信号掩码。
2.事实上 pthread_sigmask 函数和 sigprocmask 函数的行为是一样的。
pthread_sigmask函数将调用sigprocmask函数(内核源码)
[4]注意
1.如果阻塞了某个信号A, 然后调用pause。在程序的执行过程中如果一直给进程发送
信号A,pause函数将不会返回, 因为发送的信号都被阻塞。
2.对于信号集中阻塞的不可靠信号a, 在阻塞过程中, 发送多个信号a时,之前挂起的信号a会被抛弃;
解除阻塞后, 最终发送到目标进程的信号a只有一个。
3.对于信号集中阻塞的可靠信号b,在阻塞过程中, 发送多个信号a时,会创建一个队列来管理阻塞的信号;
解除阻塞后, 最终发送到目标进程的信号b = 信号b的发送次数。
4.SIGKILL 信号和 SIGSTOP 信号不能被阻塞。(设置信号集时会被内核剔除)
[5]示例代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define CUR printf("cur:%d\n", __LINE__)
int i=0, j=0;
void sig_ctlc(int sig) // sighandler_t
{
printf("is %d enter: sig_ctlc\n", ++i);
}
void sig_min1(int sig) // sighandler_t
{
printf("is %d enter: sig_min1\n", ++j);
}
int main(int argc, char **argv) // 为了简化代码, 将不判断返回值
{
int ret;
sigset_t set; // 创建线程阻塞信号集
// 安装信号
if(SIG_ERR == signal(SIGINT, sig_ctlc)) // 安装不可靠型号
perror("SIGINT install err\n");
if(SIG_ERR == signal(SIGRTMIN+1, sig_min1)) // 安装可靠型号
perror("SIGRTMIN+1 install err\n");
// 设置阻塞信号集
sigemptyset(&set); // 初始化信号集为空
sigaddset(&set, SIGINT); // 信号集中添加SIGINT信号
sigaddset(&set, SIGRTMIN+1); // 信号集中添加SIGRTMIN+1信号
sigaddset(&set, SIGKILL); // 设置不会成功, 会被内核剔除
sigaddset(&set, SIGSTOP); // 设置不会成功, 会被内核剔除
sigprocmask(SIG_BLOCK, &set, NULL); // 设置阻塞信号集为原信号集加上SIGINT.SIGRTMIN+1
// 分别发送2个SIGINT和SIGRTMIN+1信号
kill(getpid(), SIGINT);
kill(getpid(), SIGINT);
kill(getpid(), SIGRTMIN+1);
kill(getpid(), SIGRTMIN+1);
printf("%d :send sig: SIGINT SIGRTMIN+1\n", __LINE__); // 50行
// 解除阻塞信号SIGINT.SIGRTMIN+1
sigemptyset(&set); // 初始化信号集为空
sigaddset(&set, SIGINT); // 信号集中删除SIGINT信号
sigaddset(&set, SIGRTMIN+1); // 信号集中删除SIGRTMIN+1信号
sigprocmask(SIG_UNBLOCK, &set, NULL); // 设置阻塞信号集为原信号集中删除SIGINT.SIGRTMIN+1
// 这里会先去处理信号(同类信号只处理一个), 再往下执行
printf("remove end\n");
// 如果在这儿发送SIGKILL能够使程序退出, 就说明SIGKILL被阻塞成功
// 如果发送SIGSTOP信号后,能够发送SIGCONT信号显示"[1]+ Stopped ", 说明SIGSTOP信号发送成功
while(1);
return 0;
}
/* 执行结果:
book@gui_hua_shu:$ ./a.out &
book@gui_hua_shu:$ 50 :send sig: SIGINT SIGRTMIN+1
is 1 enter: sig_min1
is 2 enter: sig_min1
is 1 enter: sig_ctlc
remove end
killall -9 a.out // 程序退出了
// 再次运行程序
book@gui_hua_shu:$ ./a.out &
book@gui_hua_shu:$ killall -17 a.out // 发送暂停信号
book@gui_hua_shu:$ killall -19 a.out // 发送继续执行信号
[1]+ Stopped ./a.out // 程序成功继续执行
结论:
1.对于信号集中阻塞的不可靠信号a, 在阻塞过程中, 发送多个信号a时,之前挂起的信号a会被抛弃;
解除阻塞后, 最终发送到目标进程的信号a只有一个
2.对于信号集中阻塞的可靠信号b,在阻塞过程中, 发送多个信号a时,会创建一个队列来管理阻塞的信号;
解除阻塞后, 最终发送到目标进程的信号b = 信号b的发送次数
3.SIGKILL和SIGSTOP信号不能被阻塞(设置信号集时, 若有信号集中这两个信号,会被内核从信号集中剔除)
*/