Linux入门:信号(三)——捕捉信号
来源:互联网 发布:php array indexof 编辑:程序博客网 时间:2024/04/28 18:13
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。
一、内核如何实现信号的捕捉,例如SIGQUIT函数:
1.用户注册了SIGQUIT信号的处理函数sighandler;
2.当前正在执行main函数,这时发生了中断或异常切换到内核态;
3.在中断处理完成后要返回到用户的main函数之前检查到有SIGQUIT信号递达;
4.内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler函数和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程;
5.sighandler函数返回后自动执行系统调用sigreturn再次进入内核态;
6.如果没有新的信号递达则返回用户态恢复main函数的上下文继续执行。
二、sigaction
sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功返回0,出错则返回-1.
act 和oact指针指向struct sigaction结构体 :
struct sigaction {
void (*sa_handler)(int);
void (sa_sigaction)(int, siginfo_t , void *);
sigset_t sa_mask; // 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)
int sa_flags; //默认设置为零
void (*sa_restorer)(void);
};
sa_restorer:该元素是过时的,不应该使用,POSIX.1标准将不指定该元素。(弃用)
sa_sigaction:当sa_flags被指定为SA_SIGINFO标志时,使用该信号处理程序。(很少使用)
将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL 表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册 了一个信号处理函 数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信 号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main 函数调用,而是被系统所调用。
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函 数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产 生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则 用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
三、pause
int pause(void); //pause函数使调用进程挂起直到有信号递达(只有出错返回值)
下面用alarm和pause函数实现一个mysleep函数:
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信号以前的处理 动作。
程序代码:
#include <stdio.h>#include <signal.h>void handler(){}int mysleep(int time){struct sigaction act, oact;act.sa_handler = handler;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGALRM, &act, &oact);//调用alarm设定闹钟int ret = alarm(time);//调用pause等待,内核运行别的进程pause();sigaction(SIGALRM, &oact, NULL);//取消闹钟ret = alarm(0);//ret>0时表示休眠失败return ret;}int main(){while(1){mysleep(2);printf("I am waking up!\n");} return 0;}
运行结果:
现在重新审视一下mysleep程序,虽然alarm(time)紧接着的下一行就是pause,但是无法保证pause函数一定会在alarm被调用time秒后执行。由于异步事件在任何时候都有可能发生,所以如果我们写程序时不考虑周密,就可能因为时序问题而发生错误。这叫做竞态条件。
因此在上述程序中,可以采用sigsuspend函数将“解除信号屏蔽”和“挂起等待信号”这两步合为一个原子操作。四个suspend包含了pause的等待功能同时解决了竞态条件问题。
int sigsuspend(const sigset_t *sigmask); //执行了一个信号处理函数后才返回,返回值为-1。
调用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时 解除对某 个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原 来的值,如果原来对该信号是屏蔽的,从sigsuspend返回后仍然是屏蔽的。
改进后的程序代码:
#include <stdio.h>#include <signal.h>void handler(){}int mysleep(int time){struct sigaction act, oact;sigset_t newmask, oldmask, susmask;act.sa_handler = handler;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGALRM, &act, &oact);sigemptyset(&newmask);sigprocmask(SIG_BLOCK, &newmask, &oldmask);//调用alarm设定闹钟int ret = alarm(time);susmask = oldmask;sigdelset(&susmask, SIGALRM);//调用pause等待,内核运行别的进程//pause();sigsuspend(&susmask);ret = alarm(0);sigaction(SIGALRM, &oact, NULL);sigprocmask(SIG_SETMASK, &oldmask, NULL);//取消闹钟//ret>0时表示休眠失败return ret;}int main(){while(1){mysleep(2);printf("I am waking up!\n");} return 0;}
- Linux入门:信号(三)——捕捉信号
- 信号捕捉(三)
- shell——trap捕捉信号(附信号表)
- shell——trap捕捉信号(附信号表)
- shell——trap捕捉信号(附信号表)
- Linux入门:信号(二)——阻塞信号
- Linux下的信号(三)----捕捉信号与sleep模拟
- Linux 信号捕捉trap
- linux trap 捕捉信号
- Linux捕捉信号相关
- linux之信号捕捉
- Linux信号捕捉
- Linux信号捕捉及其处理
- linux进程如何捕捉信号
- 信号捕捉
- Linux入门:信号(一)——基本概念
- Linux 信号机制(三)
- Linux信号—可靠信号与不可靠信号实践
- 【转】俗话说。。俗话又说。。
- Python三级菜单初学->脑残版
- 【转】有关Linux 50个趣味名人名言
- C++ 类的const常量,Static变量和…
- epoll的详解
- Linux入门:信号(三)——捕捉信号
- 【转】如何在WIN7,VISTA中建立扩…
- 打包报错 Cannot merge new index 66017 into a non-jumbo instruction!
- vue.js的computed,filter,get,set的用法及区别
- 如何去除右键中的nvidia选项
- linux PS1 提示符定义
- 【转】【转】安装linux mint9(多…
- 微信小程序资讯、教程和DEMO合集(6月9日更新)
- [转]Ubuntu 10.04下MPICH2配置详解