我们知道一个信号从产生(Generation)到实际执行这个信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block )某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
需要注意的的是:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
画一个简单的示意图总结如下:
我们还知道在每个进程的PCB中有三张表来描述和记录信号的相关信息,其中block表记录被当前进程阻塞的信号,用pending表记录在当前进程下处于未决状态的信号,用handler表记录对应信号的处理函数地址(包括用户自定义函数),所以由于他们的功能我们可以确定pending表和block表的数据结构都是位图,比特位的编号对应信号的办好,比特位的值对应是否接收到信号,而handler 是一个函数指针数组,数组的每个元素存放着一个函数指针,用以递达信号。
认识一下信号的相关操作函数(以下函数都在signal.h头文件中):
int sigempty(sigset_t *set); // 清空信号集set
int sigfillset(sigset_t *set); // 填满信号集,即让set包含所有的信号
int sigaddset(sigset_t *set, int signo);
// 在set中增加signo信号
int sigdelset(sigset_t *set, int signo);
// 在set中去掉signo信号
int sigprocmask(int how, const sigset_t set, sigset_t oset);
// 若oset非空,则进程的当前信号屏蔽字通过oset返回,若set是一个
// 非空指针,着参数how指示如何修改当前信号的屏蔽字,how可以取三个值:
SIG_BLOCK:增加一个信号。
SIG_UNBLOCK:解除一个信号。
SIG_SETMASK:该进程的信号将被set信号集取代。
int sigismember(const sigset_t *set, int signo);
// 判断信号是否在set所指向的集合中
int sigpengding(sigset_t *set);
// 该函数返回信号集,该信号通过set参数返回。
现在我们编写如下代码:
结果为:
程序运行时,每秒钟把各信号的未决状态打印一遍,由于我们阻塞SIGINT信号,按Ctrl-C将会使SIGINT信号处于未决状态,按Ctrl-\仍然可以终止程序,因为SIGQUIT信号没有阻塞。