实现睡眠函数mysleep

本文讨论了普通版本的mysleep函数存在的时序问题,导致在高优先级进程执行时可能会引发错误。通过分析问题的根源,即竞态条件,作者介绍了如何使用sigsuspend函数来规避这种问题,确保在对时序要求严格的场景下正确实现mysleep。


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

vim mysleep

vim Makefile

运行结果如下:

我们可以发现当我们的代码运行以后,屏幕上输出很多的 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。

两者的区别:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值