信号捕获之pause函数和竟态条件(mysleep)

来源:互联网 发布:想购买个4星的淘宝店铺 编辑:程序博客网 时间:2024/06/04 19:37

相关博客

Linux下的信号(一)

http://blog.csdn.NET/double_happiness/article/details/72848372

Linux下的信号(二)

http://blog.csdn.Net/double_happiness/article/details/72897148


pause函数


#include <unistd.h>int pause(void);


功能:是调用进程挂起直到信号递达
返回值:只有出错返回值,没有正确返回值

信号的处理动作与pause的返回状态说明


1)默认:终止进程,pause函数没有机会返回;

2)忽略:进程继续处于挂起状态,pause不返回;

3)自定义动作:调用自定义处理函数后返回-1,errno设置为EINTR;


下面通过模拟实现sleep函数来说明这种切换


1)main函数调用my_sleep函数,后者调用sigaction注册了SIGALRM信号的处理函数handler。

2)调用alarm(times)设定闹钟。

3)调用pause等待,内核切换到别的进程运行。

4)times秒之后,闹钟超时,内核发SIGALRM给这个进程。

5)从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数是handler。

6)切换到用户态执行handler函数,进入handler函数时SIGALRM信号被自动屏蔽, 从handler函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入内核,再返回用户态继续执行进程的主控制流程。

7) pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理动作。


普通版的mysleep代码实现:(利用alarm和pause函数实现)


#include<stdio.h>#include<unistd.h>#include<unistd.h>void handler(int sig){        printf("my sig is %d\n",sig);}int mysleep(int time){        struct sigaction new,old;        int usleep=0;        new.sa_handler=handler;        sigemptyset(&new.sa_mask);        new.sa_flags=0;        sigaction(SIGALRM,&new,&old);        alarm(time);        pause();        usleep=alarm(0);        sigaction(SIGALRM,&new,NULL);        return usleep;}int main(){        while(1){                mysleep(3);                printf("sleep over\n");        }        return 0;}

运行结果:




结果分析:


首先程序如我们所预期的一样,能看到的效果是每隔三秒执行有一次,看似实现了sleep函数的功能,再此对于handler方法采用的是打印其signum,但是上面的代码存在线程安全问题,当不能执行流来运行,就会因为重入问题导致程序结果出现我们所不期待的结果。


竟态条件


上面代码问题的根本原因是系统运行的时序(Timing)并不像我们写程序时所设想的那样。虽然alarm(times)紧接着的下一行就是pause(),但是无法保证pause()一定会在调用alarm(times)之后的times秒之内被调用。由于异步事件在任何时候都有可能发生(这里的异步事件指出现更高优先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题而导致错误,这叫做竞态条件 (Race Condition)。

sigsuspend


#include <signal.h>int sigsuspend(const sigset_t *mask);

 

说明:和pause一样,sigsuspend没有成功返回值,只有执行了一个信号处理函数之后sigsuspend才返回,返回值为-1,errno设置为EINTR。调用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时解除对某个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值,如果原来对该信号是屏蔽的,从sigsuspend返回后仍然是屏蔽的。


规避竞态条件的mysleep实现:


//避免静态条件的mysleep实现#include<stdio.h>#include<signal.h>#include<unistd.h>void handler(int sig){        printf("my sig is %d\n",sig);}int mysleep(int time){        struct sigaction new,old;        sigset_t newmask,oldmask,suspmask;        int usleep=0;        new.sa_handler=handler;        sigemptyset(&new.sa_mask);        new.sa_flags=0;        sigaction(SIGALRM,&new,&old);        sigemptyset(&newmask);        sigaddset(&newmask,SIGALRM);        sigprocmask(SIG_BLOCK,&newmask,&oldmask);        alarm(time);        suspmask=oldmask;        sigdelset(&suspmask,SIGALRM);        sigsuspend(&suspmask);        usleep=alarm(0);        sigaction(SIGALRM,&old,NULL);        sigprocmask(SIG_SETMASK,&oldmask,NULL);        return usleep;}int main(){        while(1){                mysleep(3);                printf("sleep over\n");        }        return 0;}


运行结果:




结果说明:


首先代码运行起来的现象还是和上面没有什么区别,不过和上面普通版的mysleep实现相比:

如果在调用my_sleep函数时SIGALRM信号没有屏蔽:
1)调用sigprocmask(SIG_BLOCK,&newmask, &oldmask)时,屏蔽SIGALRM。
2)调用sigsuspend(&suspmask)时,解除对SIGALRM的屏蔽,然后挂起等待。
3)SIGALRM递达后suspend返回,自动恢复原来的屏蔽字,也就是再次屏蔽SIGALRM。
4)调用sigprocmask(SIG_SETMASK, &oldmask, NULL)时,再次解除对SIGALRM的屏蔽。

阅读全文
0 0
原创粉丝点击