模拟实现sleep函数

来源:互联网 发布:got7直播软件 编辑:程序博客网 时间:2024/05/19 13:26

一、普通版本下的sleep函数
前面我们学习了pause函数,知道了pause函数的功能是为了挂起当前的进程,直到有信号递达。有问题的可以参见我的上一篇博客:这篇博客将信号讲的很清楚,其中对于pause函数也做了相应的说明。
linux下的信号
今天我就是用这个pause函数来实现一个自己的sleep函数。
具体实现原理如图所示:
这里写图片描述
实现过程如下:
1、main函数调用了my_sleep函数,mysleep函数注册了SIGALRM信号的处理函数handler。
2、alarm函数设定的是闹钟,调用pause函数进行等待,在times秒之后内核发送SIGALRM信号给进程。
3、发现SIGALRM信号调用它的handler函数,切换到用户态执行handler函数,进入handler函数时SIGALRM信号被自动屏蔽, 从handler函数返回时SIGALRM信号自动解除屏蔽。再返回用户态
4、pause函数返回-1,调用alarm(0)取消闹钟调用sigaction恢复SIGALRM信号以前的处理动作。
具体代码如下:

#include<stdio.h>#include<unistd.h>#include<signal.h>void hander(int sig){}int mysleep(int time){    struct sigaction act,oact;    act.sa_handler = hander;    sigemptyset(&act.sa_mask);    act.sa_flags = 0;    sigaction(SIGALRM,&act,&oact);    alarm(time);    pause();    sigaction(SIGALRM,&oact,&act);    int ret = alarm(0);    return ret;}int main(){    while(1)    {        int ret = mysleep(3);        printf("mysleep is over ...%d\n",ret);    }    return 0;}

这里写图片描述
代码中存在的问题:
调用alarm(seconds)时,这时并没有执行下一条指令pause()使进程挂起,这时若有内核调度优先级更高的进程取代当前进程执行,并且优先级更高的进程有很多个,每个都要执行很长时间。seconds秒之后闹钟超时了,内核发送SIGALRM信号给被取代切出的进程,其信号处于未决状态,当优先级更高的进程执行完了,内核调度会使被取代切出进程执行,这时SIGALRM信号递达,执行其自定义捕捉函数之后再次进入内核;最后返回这个进程的主控制流程,alarm(seconds)返回,再调用pause()挂起等待。

别的进程优先级高影响了此进程调度时间,使alarm()函数seconds秒发送的SIGALRM信号已经处理完成,所以pause使进程挂起后将不会在收到自定义捕捉的信号。

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

sigsuspend:

int sigsuspend(const sigset_t *mask);

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

#include<stdio.h>#include<unistd.h>#include<signal.h>void hander(int sig){}int mysleep(int time){    struct sigaction act,oact;    act.sa_handler = hander;    sigemptyset(&act.sa_mask);    act.sa_flags = 0;    sigaction(SIGALRM,&act,&oact);    sigset_t newset,oldset,sigmask;    sigemptyset(&newset);    sigemptyset(&oldset);    sigaddset(&newset,SIGALRM);    sigprocmask(SIG_BLOCK,&newset,&oldset);    alarm(time);    sigmask = oldset;    sigdelset(&sigmask,SIGALRM);    sigsuspend(&sigmask);    sigaction(SIGALRM,&oact,&act);    sigprocmask(SIG_SETMASK,&oldset,&newset);    int ret = alarm(0);    return ret;}int main(){    while(1)    {        int ret = mysleep(3);        printf("mysleep is over ...%d\n",ret);    }    return 0;}


这里写图片描述

原创粉丝点击