简单的企业网站建设教程,seo优化的优点,商业网站导航怎么做,wordpress糗事百科目录
1、阻塞信号
1.1、信号其他相关常见概念
1.2、在内核中的表示
1.3、sigset_t
1.4、信号集操作函数
2、捕捉信号
2.1、内核如何捕捉信号
5.2、sigaction 1、阻塞信号
1.1、信号其他相关常见概念 实际执行信号的处理动作被称为信号递达#xff08;Delivery#x…目录
1、阻塞信号
1.1、信号其他相关常见概念
1.2、在内核中的表示
1.3、sigset_t
1.4、信号集操作函数
2、捕捉信号
2.1、内核如何捕捉信号
5.2、sigaction 1、阻塞信号
1.1、信号其他相关常见概念 实际执行信号的处理动作被称为信号递达Delivery 信号从产生到递达之间的状态称为信号未决Pending 进程可以选择阻塞Block某个信号 被阻塞的信号产生时将保持在未决状态直到进程接触对此信号的阻塞才执行递达的动作 注意阻塞和忽略是不同的只要信号被阻塞就不会递达而忽略是在递达之后可选的一种处理动作。 1.2、在内核中的表示
信号在内核中的表示示意图 每个信号都有两个标志位分别表示阻塞block和未决pending还有一个函数指针表示处理动作。信号产生时内核在进程控制块中设置该信号的未决标志直到信号递达才清除该标志。在上图的例子中SIGHUP信号未阻塞也未生产过当它递达时执行默认处理动作。 SIGINT信号产生过但正在被阻塞所以暂时不能递达。虽然它的处理动作是忽略但在没有接触阻塞之前不能忽略这个信号因为进程仍有机会改变处理动作之后再解除阻塞。 SIGQUIT信号未产生过一旦生产SIGQUIT信号将被阻塞它的处理动作是用户自定义函数sighandler。 如果在进程解除对某信号的阻塞之前这种信号产生过多次将如何处理 POSIX.1允许系统递送改信号一次或多次。Linux是这样实现的常规信号在递达之前产生多次只计一次而实时信号在递达之前产生多次可以依次放在一个队列里。 1.3、sigset_t
从上面的图可以看出每个信号只有一个bit的未决标志非0即1不记录该信号产生了多少次阻塞表示也是这样表示的。因此未决标志和阻塞标志可以用相同的数据类型sigset_t来存储sigset_t称为信号集这个类型可以表示每个信号的“有效”或“无效”状态在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞而在未决信号集中“有效”和“无效”的含义是是该信号是否处于未决状态。后面会详细介绍各种信号集的操作。阻塞信号集也叫做当前进程的信号屏蔽字Signal Mask这里的屏蔽应该理解为阻塞而不是忽略。
1.4、信号集操作函数 sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态至于这个类型内部如何存储这些bit则依赖于系统实现从使用者的角度是不必关心的使用者只能调用以下函数来操作sigset_ t变量而不应该对它的内部数据做任何解释,比如用printf直接打印sigset_t变量是没有意义的。
#include signal.h//初始化set所指向的信号集使其中所有的信号对应bit清零
//表示该信号集不包含任何有效信号
int sigemptyset(sigset_t *set);//初始化set所指向的信号集使其中所有信号的对应bit置位
//表示该信号集的有效信号包括系统支持的所有信号
int sigfillset(sigset_t *set);//将某个信号添加到信号集中
int sigaddset(sigset_t *set, int signo);//将某个信号从信号集删除
int sigdelset(sigset_t *set, int signo);//判断某个信号是否在信号集中
int sigismember(const sigset_t *set, int signo);
注意在使用sigset_ t类型的变量之前一定要调 用sigemptyset或sigfillset做初始化使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。
这四个函数都是成功返回0出错返回-1。sigismember是一个布尔函数用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1不包含则返回0出错返回-1。
如果要通过信号集对进程的pending、block表进行相关设置还需要调用sigprocmask。
#includesignal.hint sigprocmask(int how, const sigset_t *set, sigset_t *oset);返回值:若成功则为0若出错则为-1
调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。
如果oset是非空指针则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针则更改进程的信号屏蔽字参数how指示如何更改。如果oset和set都是非空指针则先将原来的信号屏蔽字备份到oset里然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask下表说明了how参数的可选值。 #includesignal.hint sigpending(sigset_t *set);读取当前进程的未决信号集通过set参数传出。调用成功则返回0出错则返回-1。
下面用这几个函数做一个实验 1、先把2号信号屏蔽 2、kill或者键盘发送2号信号可以预见2号信号不会被递达 3、2号信号将会一直被阻塞一定一直在pending中 4、使用sigpending获取当前进程的pending信号集 #include stdio.h
#include signal.h
#include unistd.h
#include asm-generic/signal.hvoid printsigset(sigset_t *set)
{int i 0;for (;i32;i){//判断指定信号是否在目标集合中if (sigismember(set,i)){printf(1);}else {printf(0);}}printf(\n);
}int main()
{//定义信号集对象并清空初始化sigset_t set, oset;sigemptyset(set);sigemptyset(oset);sigaddset(set, SIGINT);//设置阻塞信号集阻塞SIGINT信号sigprocmask(SIG_BLOCK, set, oset);sigset_t pending;while(1){sigemptyset(pending);sigpending(pending);printsigset(pending);sleep(1);}return 0;
}可以看到我们发送信号前二号信号为0发送后2号信号为1因为当前信号无法被递达所以处于pending状态就被pending捕获并打印出来了。 2、捕捉信号
2.1、内核如何捕捉信号
如果信号的处理动作是用户自定义函数在信号递达时就调用这个函数这称为捕捉信号。由于信号处理函数的代码是在用户空间的处理过程比较复杂举例如下: 用户程序注册了 SIGQUIT 信号的处理函数 sighandler 。当前正在执行 main 函数这时发生中断或异常切换到内核态。在中断处理完毕后要返回用户态的main函数之前检查到有信号 SIGQUIT 递达。内核决定返回用户态后不是恢复 main 函数的上下文继续执行而是执行 sighandler 函数sighandler 和 main 函数使用不同的堆栈空间它们之间不存在调用和被调用的关系是两个独立的控制流程。 sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达这次再返回用户态就是恢复 main 函数的上下文继续执行了。
5.2、sigaction #include signal.h int sigaction(int signo, const struct sigaction *act, struct sigaction *oact); sigaction 函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0出错则返回 -1 。signo 是指定信号的编号。若 act 指针非空则根据 act 修改该信号的处理动作。若oact指针非空则通过oact传出该信号原来的处理动作。act 和 oact 指向sigaction结构体: 将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号赋值为常数SIG_DFL表示执行系统默认动作赋值为一个函数指针表示用自定义函数捕捉信号或者说向内核注册了一个信号处理函数该函数返回值为void可以带一个int参数通过参数可以得知当前信号的编号这样就可以用同一个函数处理多种信号。显然这也是一个回调函数不是被main函数调用而是被系统所调用。 当某个信号的处理函数被调用时内核自动将当前信号加入进程的信号屏蔽字当信号处理函数返回时自动恢复原来的信号屏蔽字这样就保证了在处理某个信号时如果这种信号再次产生那么它会被阻塞到当前处理结束为止。如果在调用信号处理函数时除了当前信号被自动屏蔽之外还希望自动屏蔽另外一些信号则用sa_mask字段说明这些需要额外屏蔽的信号当信号处 理函数返回时自动恢复原来的信号屏蔽字。
#include stdio.h
#include unistd.h
#include signal.h
#include string.hstruct sigaction act, oact;
void sigcb(int signo)
{printf(get a signal: %d\n, signo);sigaction(SIGINT, oact, NULL);
}int main()
{memset(act, 0, sizeof(act));memset(oact, 0, sizeof(oact));act.sa_handler sigcb;act.sa_flags 0;sigemptyset(act.sa_mask);sigaction(SIGINT, act, oact);while(1){printf(I am a process!\n);sleep(1);}return 0;
}这样便捕捉到了一次信号第二次不再捕捉可以正常退出。