mysleep
普通版本的mysleep:
1、 main函数调用mysleep函数,后者调用sigaction注册了SIGALRM信号的处理函数handler。
2、调用alarm(time)设定闹钟。
3、调用pause等待,内核切换到别的进程运行。
4、time秒之后,闹钟超时,内核发SIGALRM给这个进程。
5、从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数是handler。
6、切换到用户态执行handler函数,进入handler函数时SIGALRM信号被自动屏蔽, 从handler函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入 内核,再返回用户态继续执行进程的主控制流程(main函数调用的mysleep函数)。
6、切换到用户态执行handler函数,进入handler函数时SIGALRM信号被自动屏蔽, 从handler函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入 内核,再返回用户态继续执行进程的主控制流程(main函数调用的mysleep函数)。
7、pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理 动作。
(1)普通版本mysleep代码如下:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void handler(int signum)
{}
int mysleep(int time)
{
struct sigaction act,oact;
act.sa_handler=handler;
act.sa_flags=0;
sigset_t sa_mask;
sigemptyset(&sa_mask);
sigaction(SIGALRM,&act,&oact);
alarm(time);
pause();
int ret=alarm(0);
sigaction(SIGALRM,&act,NULL);
return ret;
}
int main()
{
while(1)
{
mysleep(3);
printf("I am sleep,I will sleeping 3 second\n");
}
return 0;
}
(2)规避竞态mysleep
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
int sigsuspend(const sigset_t *sigmask);
void handler(int signum)
{}
int mysleep(int time)
{
struct sigaction act,oact;
sigset_t newmask,oldmask,suspmask;
act.sa_handler=handler;
act.sa_flags=0;
sigset_t sa_mask;
sigemptyset(&sa_mask);
sigaction(SIGALRM,&act,&oact);
sigemptyset(&newmask);
sigaddset(&newmask,SIGALRM);
sigprocmask(SIG_BLOCK,&newmask,&oldmask);//屏蔽信号SIGALRM
alarm(time);
suspmask=oldmask;
sigdelset(&suspmask,SIGALRM);
sigsuspend(&suspmask);
int ret=alarm(0);
sigaction(SIGALRM,&act,NULL);
sigprocmask(SIG_SETMASK,&oldmask,NULL);
return ret;
}
int main()
{
while(1)
{
mysleep(3);
printf("I am sleep,I will sleeping 3 second\n");
}
return 0;
}
虽然两个mysleep运行的结果目前表面上都是一样的,但
系统运⾏的时序(Timing)并不像我们写程序时所设想的那样。在普通版本中,虽然alarm(time)紧接着的下⼀行就是pause(),但是无法保证pause()一定会在调用alarm(time)之后的time秒之内被调用。 被切换后,若触发SIGALRM信号,信号会立即递达,当该进程切回来的时候,接收不到SIGALRM信号,不会再次接收到该信号,线程一直处于挂起状态,得不到激活。在规避竞态版本下,对SIGALRM信号进行了屏蔽,被切出去之后,该信号也不会被递达。因为是原语操作,取消屏蔽字后 ,程序正常激活。