即日起在codingBlog上分享您的技术经验即可获得积分,积分可兑换现金哦。

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

编程语言 double_happiness 31℃ 0评论

相关博客

Linux下的信号(一)

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

Linux下的信号(二)

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


pause函数


#include 
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
#include
#include

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 
int sigsuspend(const sigset_t *mask);

 

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




规避竞态条件的mysleep实现:





//避免静态条件的mysleep实现
#include
#include
#include

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的屏蔽。


转载请注明:CodingBlog » 信号捕获之pause函数与竟态条件(mysleep)

喜欢 (0)or分享 (0)
发表我的评论
取消评论

*

表情