捕捉信号

捕捉信号
4.1. 内核如何实现信号的捕捉
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信
号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:
1. 用户程序注册了SIGQUIT信号的处理函数sighandler 。
2. 当前正在执行main 函数,这时发生中断或异常切换到内核态。
3. 在中断处理完毕后要返回用户态的main 函数之前检查到有信号SIGQUIT递达。
4. 内核决定返回用户态后不是恢复main 函数的上下文继续执行,而是执行sighandler 函
数,sighandler 和main 函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是
两个独立的控制流程。
5. sighandler 函数返回后自动执行特殊的系统调用sigreturn 再次进入内核态。

6. 如果没有新的信号要递达,这次再返回用户态就是恢复main 函数的上下文继续执行了。


4.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 结构体:
struct sigaction {
void
(*sa_handler)(int);
sigset_t sa_mask;
int
sa_flags;
/* addr of signal handler, */
/* or SIG_IGN, or SIG_DFL */
/* additional signals to block */
/* signal options, Figure 10.16 */
/* alternate handler */
void
(*sa_sigaction)(int, siginfo_t *, void *);
};
将sa_handler 赋值为常数SIG_IGN传给sigaction 表示忽略信号,赋值为常数SIG_DFL表示执行系统默
认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函
数,该函数返回值为void ,可以带一个int 参数,通过参数可以得知当前信号的编号,这样就可以
用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main 函数调用,而是被系统所调
用。
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返
回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么
它会被阻塞到当前处理结束为止。如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还
希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返
回时自动恢复原来的信号屏蔽字。
sa_flags 字段包含一些选项,本章的代码都把sa_flags 设为0,sa_sigaction 是实时信号的处理函
数,

4.3. pause
#include <unistd.h>
int pause(void);
pause 函数使调用进程挂起直到有信号递达。如果信号的处理动作是终止进程,则进程终
止,pause 函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态,pause 不返
回;如果信号的处理动作是捕捉,则调用了信号处理函数之后pause 返回-1,errno 设置为EINTR ,
所以pause 只有出错的返回值(想想以前还学过什么函数只有出错返回值?)。错误码EINTR 表
示“被信号中断”

例 33.2. mysleep
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void sig_alrm(int signo)
{
/* nothing to do */
}
unsigned int mysleep(unsigned int nsecs)
{
struct sigaction newact, oldact;
unsigned int unslept;
newact.sa_handler = sig_alrm;
sigemptyset(&newact.sa_mask);
newact.sa_flags = 0;
sigaction(SIGALRM, &newact, &oldact);
alarm(nsecs);
pause();
unslept = alarm(0);
sigaction(SIGALRM, &oldact, NULL);
return unslept;
}
int main(void)
{
while(1){
mysleep(2);
printf("Two seconds passed\n");
}
return 0;
}
1. main 函数调用mysleep函数,后者调用sigaction 注册了SIGALRM信号的处理函数sig_alrm 。
2. 调用alarm(nsecs)设定闹钟。
3. 调用pause 等待,内核切换到别的进程运行。
4. nsecs 秒之后,闹钟超时,内核发SIGALRM给这个进程。
5. 从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数
是sig_alrm 。
6. 切换到用户态执行sig_alrm 函数,进入sig_alrm 函数时SIGALRM信号被自动屏蔽,
从sig_alrm 函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn 再次进入
内核,再返回用户态继续执行进程的主控制流程(main 函数调用的mysleep函数)。
7. pause 函数返回-1,然后调用alarm(0) 取消闹钟,调用sigaction 恢复SIGALRM信号以前的处理
动作。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值