相关博客
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的屏蔽。