信号集
表示多个信号的集合
信号在内核中的表示
信号递达(Delivery):实际执行信号处理的动作。
信号未决(Pending):信号从产生到递达之间的状态。
信号阻塞(Block) :被阻塞的信号产生时将保持在未决状态,直到 进程解除对此信号的阻塞,才 执行递达的动作。
注意:
信号阻塞和信号忽略是不同的。只要信号被阻塞就不会递达,除非解除阻塞,而忽略是在递达之后 可选的一种处理动作。
一个信号处于Pending状态,屏蔽之后,它永远不会被Delivery 。
一个信号是否立即Delivery ,与Block没有绝对关
3张表的存储:
pending表: 用4个字节的位图表示,位图的位置表示信号编号,内容表示是否pending。
block表: 用4个字节的位图表示,位图的位置表示信号编号,内容表示是否block。
handler表: 是一个句柄函数指针,数组即可表示,下标表示信号编号,内容表示信号处理的动作,为NULL表示没有处理该信号。
分析上图中的信号:
1〉SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
2〉SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
3〉 SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。
信号集接口函数
sigemptyset | 置空一个信号集 |
sigfillset | 填充满一个信号集 |
sigaddset | 将一个信号加入信号集 |
sigdelset | 将一个信号从信号集删除 |
sigismember | 检查一个信号集中是否有这个信号 |
#include <signal.h> int sigemptyset(sigset_t *set);//将set集合置空 int sigaddset(sigset_t *set,int signo);//将signo信号加入到set集合 int sigdelset(sigset_t *set,int signo);//从set集合中移除signo信号 int sigfillset(sigset_t *set); //将所有信号加入set集合
返回值:
若成功,返回0
若出错,返回-1
int sigismember(const sigset_t *set, int signum); //测试参数signum 代表的信号是否已加入至参数set信号集里
返回值:
若成功,返回0
若出错,返回-1
int sigdelset(sigset_t *set,int signo); //设定对信号屏蔽集内的信号的处理方式(阻塞或不阻塞)。
参数:
how:用于指定信号修改的方式,可能选择有三种:
SIG_BLOCK //加入信号到进程屏蔽。
SIG_UNBLOCK //从进程屏蔽里将信号删除。
SIG_SETMASK //将set的值设定为新的进程屏蔽。
set:指向信号集的指针,在此专指新设的信号集,如果仅想读取现在的屏蔽值,可将其置为NULL。
oldset:也是指向信号集的指针,在此存放原来的信号集。
返回值:
若成功,返回0
若出错,返回-1,errno被设为EINVAL。
int sigsuspend(const sigset_t *sigmask); //该函数通过将进程的屏蔽字替换为由参数sigmask给出的信号集,然后挂起进程的执行。注意操作的先后顺序,是先替换再挂起程序的执行。程序将在信号处理函数执行完毕后继续执行。 /* sigsuspend的整个原子操作过程为: (1) 设置新的mask阻塞当前进程; (2) 收到信号,恢复原先mask; (3) 调用该进程设置的信号处理函数; (4) 待信号处理函数返回后,sigsuspend返回。 */
参数说明
sigmask 希望屏蔽的信号
返回值
若接收到信号终止了程序,sigsuspend()就不会返回。
若出错(接收到的信号没有终止程序),返回-1,并将errno设置为EINTR。
使用场景:
sigsuspend() 函数可以更改进程的信号屏蔽字可以阻塞所选择的信号,或解除对它们的阻塞,使用这种技术可以保护不希望由信号中断的代码临界区。
如果希望对一个信号解除阻塞,然后pause等待以前被阻塞的信号发生,那么必须使用 sigsuspend() 函数, pause() 函数无法达成上述目的.
- 阻塞信号
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) err_sys("SIG_BLOCK error");
- 解除阻塞
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) err_sys("SIG_SETMASK error");
- 等待信号
pause();
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); /* Prototype for the underlying system call */ //可以根据参数指定的方法修改进程的信号屏蔽字。 //新的信号屏蔽字由参数set(非空)指定,而原先的信号屏蔽字将保存在oset(非空)中。 //如果set为空,则how没有意义,但此时调用该函数,如果oset不为空,则把当前信号屏蔽字保存到oset中。 /* 底层系统调用的原型 */ int rt_sigprocmask(int how, const kernel_sigset_t *set,kernel_sigset_t *oldset, size_t sigsetsize); /* Prototype for the legacy system call (deprecated) */ int sigpending(sigset_t *set); //获取信号集,查询被搁置的信号
参数:
how:不同取值及操作如下所示:
注:调用这个函数才能改变进程的屏蔽字,之前的函数都是为改变一个变量的值而已,并不会真正影响进程的屏蔽字。
SIG_BLOCK : 附加set到阻塞表,原来的保存在到oldset
SIG_UNBLOCK:从阻塞表中删除set中的信号,原来的保存到oldset
SIG_SETMASK:清空阻塞表并设置为set,原来的保存到oldset
返回值:
若成功,返回0
若出错(how取值无效返回-1),返回-1,并设置errno为EINVAL。