编写mysleep

来源:互联网 发布:淘宝白底图片制作 编辑:程序博客网 时间:2024/06/05 06:14

alarm函数

使用alarm函数可以设置一个时间值(闹钟),在将来的某个时刻这个值会被超过。当所设置的时间超过后,产生SIGALRM信号。如果不忽略或不捕捉此信号,则其默认动作是终止该进程。参数seconds的值是秒数,经历了指定的seconds秒后会产生信号SIGALRM。


pause函数

pause函数使调用进程挂起直至捕捉到一个信号,只有执行一个信号处理程序并从其返回时,pause函数返回-1。


sigaction  信号捕捉函数

sigactoin函数的功能是检查或修改与指定信号相关联的处理动作。signum是要检测或修改的具体动作的信号的编号。若act指针非空,则要修改其动作,若oldact指针非空,则系统返回该信号的原先动作。


1  普通版本mysleep代码如下


 

运行结果


1.main函数调用mysleep函数,后者调用sigaction注册了SIGALRM信号的处理函数sig_alrm。
2. 调用alarm(seconds)设定闹钟。
3. 调用pause等待,内核切换到别的进程运行。
4. seconds秒之后,闹钟超时,内核发SIGALRM给这个进程。
5. 从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数是handler。
6. 切换到用户态执行handler函数,进入handler函数时SIGALRM信号被自动屏蔽,从handler函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入内核,再返回用户态继续执行进程的主控制流程(main函数调用的mysleep函数)。
7. pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理动作。
需要注意的是虽然handler函数什么都没干,但还是得注册作为SIGALRM的处理函数,因为SIGALRM信号的默认处理是终止进程,这也是在mysleep函数返回时要恢复SIGALRM信号原来的sigaction的原因。此外,mysleep函数的返回值表示“未睡到”的时间,即unslept,当尚未计时到seconds而pause函数先被其他信号处理函数所中断返回,在外界看来就是在sleep期间被其他信号处理函数中断了,则mysleep返回非0值,即unslept。

普通版本的mysleep存在一个问题:pause函数使调用进程挂起知道捕捉到一个信号,在调用alam和pause之间有个竞态条件,在一个繁忙的系统,alrm调用pause之前可能超时,并调用了信号处理程序。如果发生这种情况,则在调用pause之后,如果没有捕捉到其它信号,该调用者会被永远挂起。

竞态条件:由于异步事件在任何时候都有可能发生(这里的异步事件指出现更高优先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题而导致错误。

2 规避静态条件的mysleep

解决该问题的方法需要将“解除信号屏蔽”和“挂起等待信号”这两步能合并成一个原子操作,而sigsuspend函数刚好有这个功能。sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题,在对时序要求严格的场合下都应该调用sigsuspend而不是pause


代码实现



运行结果

如果在调用mysleep函数时SIGALRM信号没有屏蔽 :

1. 调用sigprocmask(SIG_BLOCK, &newmask, &oldmask);时屏蔽SIGALRM。

2. 调用sigsuspend(&suspmask);时解除对SIGALRM的屏蔽,然后挂起等待待。

3. SIGALRM递达后suspend返回,自动恢复原来的屏蔽字,也就是再次屏蔽SIGALRM。

4. 调用sigprocmask(SIG_SETMASK, &oldmask, NULL);时再次解除对SIGALRM的屏蔽

原创粉丝点击