实现睡眠函数mysleep



1. 普通版本的 mysleep 函数(有 bug 存在)

vim mysleep

实现睡眠函数mysleep_第1张图片

vim Makefile

运行结果如下:

实现睡眠函数mysleep_第2张图片

我们可以发现当我们的代码运行以后,屏幕上输出很多的 using mysleep sleep! ,而且在最后一行每隔三秒打印一次,且一直打印直到我们强制停止。

审视 “mysleep” 程序,设想这样的时序:

① 注册 SIGALRM 信号的处理函数。

② 调用 alarm(nsecs) 设定闹钟。

③ 内核调度优先级更高的进程取代当前进程执行,并且优先级更高的进程有很多个,每个都要执行很长时间。

④ nsecs 秒钟之后闹钟超时了,内核发送 SIGALRM 信号给这个进程,处于未决状态。

⑤ 优先级更高的进程执行完了,内核要调度回这个进程执行。SIGALRM 信号递达,执行处理函数 sig_alrm 之后再次进入内核。

⑥ 返回这个进程的主控制流程,alarm(nsecs) 返回,调用 pause() 挂起等待。

⑦ 可是 SIGALRM 信号已经处理完了,还等待什么呢?

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

2. 规避竞态条件的 mysleep

vim sleep.c

实现睡眠函数mysleep_第3张图片

实现睡眠函数mysleep_第4张图片

运行结果如下:

实现睡眠函数mysleep_第5张图片

结果相同,证明我们也实现了 mysleep。

两者的区别:

        第一种 mysleep 的实现我们说存在时序问题,会在某种程度上产生 bug,那么第二种方法是如何规避的呢?它将“解除信号屏蔽”和“挂起等待信号”这两步能合并成一个原子操作,是因为调用了 sigsuspend 函数,sigsuspend 包含了 pause 的挂起等待功能,同时解决了竞态条件的问题,因此在对时序要求严格的场合下都应该调用 sigsuspend 而不是pause。

你可能感兴趣的:(实现睡眠函数mysleep)