模拟实现sleep函数

来源:互联网 发布:安卓限速软件 编辑:程序博客网 时间:2024/05/17 19:19

模拟实现sleep介绍三个函数alarm函数,sigaction函数,以及pause函数。

alarm函数:

       #include <unistd.h>
       unsigned int alarm(unsigned int seconds);

调用alarm函数可以设定一个闹钟,告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。这个函数的返回值是0(意味着闹钟准时响了)或者是以前设定的闹钟时间还余下的秒数(可能由于某些原因,闹钟提前响了)。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍是以前设定的闹钟时间还余下的秒数。如果余下的秒数为0,就是说操作系统senconds秒之后准时的给当前进程发送了SIGALRM信号。如果余下的秒数大于0,说明操作系统由于某些原因在没有到senconds秒的时候给当前进程发送了SIGALRM信号,而返回值就是离senconds秒还差多少秒。

信号捕捉函数:

      #include <signal.h>
      int sigaction(int signo, const struct sigaction *act, structsigaction *oact);

      sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体。

pause函数:

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

     pause函数使调用进程挂起直到有信号递达。如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR, 所以pause只有出错的返回值。

      开始编写mysleep,main函数调用mysleep函数,后者调用sigaction注册了SIGALRM信号的处理函数myheadler,调用alarm(nsecs)设定闹钟。调用pause等待,内核切换到别的进程运行nsecs秒之后,内核发SIGALRM给这个进程。然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理 动作。接下来看一下代码实现:

1. #include<stdio.h>  

2. #include<signal.h>  

3. #include<unistd.h>  

4. void  myhandler(int sig)  

5. {}  

6.   

7.   

8. int mysleep(int timeout)  

9. {  

10.     struct sigaction act,oact;  

11.     act.sa_handler = myhandler;  

12.     sigemptyset(&act.sa_mask);  

13.     act.sa_flags = 0;  

14.     sigaction(SIGALRM,&act,NULL);  

15.   

16.     alarm(timeout);  

17.     pause();  

18.     int ret = alarm(0);  

19.     sigaction(SIGALRM,&oact,NULL);  

20.     return ret;  

21. }  

22.   

23.   

24. int main()  

25. {  

26.     while(1){

27. printf("using mysleep!\n");

28. mysleep(3);

29. } 

30.     return 0;  

31. }  

       假设注册SIGALRM信号的处理函数,调用alarm(nsecs)设定闹钟,内核调度优先级更高的进程取代当前进程执行,并且优先级更高的进程有很多个,每个都要执行很长时间 nsecs秒钟之后闹钟超时了,内核发送SIGALRM信号给这个进程,处于未决状态。优先级更高的进程执行完了,内核要调度回这个进程执行。SIGALRM信号递达,执行处理函 数sig_alrm之后再次进入内核。返回这个进程的主控制流程,alarm(nsecs)返回,调用pause()挂起等待。可是SIGALRM信号已经处理完了。那么这个进程就会一直挂起等待了。
       由于异步事件在任何时候都有可能发生(这里的异步事件指出现更高优 先级的进程),由于时序问题而导致错误,叫做竞态条件 (Race Condition)。

       解决:

sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题,在对时序要求严格的场合下都应该调⽤用sigsuspend而不是pause。

         #include <signal.h>
        int sigsuspend(const sigset_t *sigmask);
pause一样,sigsuspend没有成功返回值,只有执行了一个信号处理函数之后sigsuspend才返回,返回值为-1,errno设置为EINTR。调用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时解除对某个信号屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值,如果原来对该信号是屏蔽的,从sigsuspend返回后仍然是屏蔽的。
         以下用sigsuspend重新实现mysleep函数:

1. #include<stdio.h>  

2. #include<signal.h>  

3. #include<unistd.h>  

4. void handler(int sig)  

5. {}  

6.   

7.   

8. int mysleep(int timeout)  

9. {  

10.     struct sigaction act,oact;  

11.     sigset_t newmask,oldmask,suspmask;  

12.   

13.     act.sa_handler = handler;  

14.     sigemptyset(&act.sa_mask);  

15.     act.sa_flags = 0;  

16.     sigaction(SIGALRM,&act,&oact);  

17.   

18.     sigemptyset(&newmask);  

19.     sigaddset(&newmask,SIGALRM);  

20.     sigprocmask(SIG_BLOCK,&newmask,&oldmask);  

21.   

22.     alarm(timeout);  

23.   

24.     suspmask = oldmask;  

25.     sigdelset(&suspmask,SIGALRM);  

26.       

27.     sigsuspend(&suspmask);  

28.   

29.     int ret = alarm(0);  

30.     sigaction(SIGALRM,&oact,NULL);  

31.     sigprocmask(SIG_SETMASK,&oldmask,NULL);  

32.     return ret;  

33. }  

34.   

35. int main()  

36. {  

37.     while(1){

38. printf("using mysleep!\n");

39. mysleep(3);

40. }

41.     return 0;  

42. }  

 

原创粉丝点击