修改系统时间,导致 sem_timedwait 一直阻塞的问题解决和分析
介绍
最近修复项目问题时,发现当系统时间往前修改后,会导致sem_timedwait
函数一直阻塞。通过搜索了发现int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
传入的第二个阻塞时间参数是绝对的时间戳,那么该函数是存在缺陷的。
sem_timedwait存在的缺陷的理由:
假设当前系统时间是1565000000(2019-08-05 18:13:20)
,sem_timedwait
传入的阻塞等待的时间戳是1565000100(2019-08-05 18:15:00)
,那么sem_timedwait
就需要阻塞1分40秒(100秒)
,若在sem_timedwait
阻塞过程中,中途将系统时间往前修改成1500000000(2017-07-14 10:40:00)
,那么sem_timedwait
此时就会阻塞2年多! 这就是sem_timedwait
存在的缺陷!!
sem_timedwait函数介绍
如果信号量大于0,则对信号量进行递减操作并立马返回正常
如果信号量小于0,则阻塞等待,当阻塞超时时返回失败(
errno
设置为ETIMEDOUT
)
第二个参数abs_timeout
参数指向一个指定绝对超时时刻的结构,这个结果由自 Epoch,1970-01-01 00:00:00 +0000(UTC)
秒数和纳秒数构成。这个结构定义如下
解决方法
可以通过sem_trywait
+ usleep
的方式来实现与sem_timedwait
函数的类似功能,并且不会发生因系统时间往前改而出现一直阻塞的问题。
sem_trywait函数介绍
函数 sem_trywait()
和sem_wait()
有一点不同,即如果信号量的当前值为0,则返回错误而不是阻塞调用。错误值errno设置为EAGAIN。sem_trywait()
其实是sem_wait()
的非阻塞版本。
执行成功返回0,执行失败返回 -1且信号量的值保持不变。
sem_trywait + usleep的方式实现
主要实现的思路:
sem_trywait
函数不管信号量为0或不为0都会立刻返回,当函数正常返回的时候就不usleep
;当函数不正常返回时就通过usleep
来实现延时,具体是实现方式如下代码中的bool Wait( size_t timeout )
函数:
测试结果:
总结
尽量不要使用sem_timedwait
函数来实现延时等待的功能,若要使用该延时等待的功能,建议使用sem_trywait
+usleep
实现的延时阻塞!
版权声明: 本文为 InfoQ 作者【小林coding】的原创文章。
原文链接:【http://xie.infoq.cn/article/1fb783d9bea788f9433d8946f】。文章转载请联系作者。
评论