C语言:修改系统时间,导致sem_timedwait 一直阻塞的问题解决和分析

博客分析了在系统时间被往前修改后,sem_timedwait函数可能长时间阻塞的问题,解释了其原因在于使用了绝对时间戳作为超时参数。解决方案是使用sem_trywait配合usleep来避免此类问题。建议避免使用sem_timedwait进行延时等待,推荐使用sem_trywait实现。
摘要由CSDN通过智能技术生成

一、网络摘文

参考目录: https://zhuanlan.zhihu.com/p/83581365

1. 背景介绍及原因分析

最近修复项目问题时,发现当系统时间往前修改后,会导致sem_timedwait函数一直阻塞。通过搜索发现

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

传入的第二个阻塞时间参数是绝对的时间戳,那么该函数是存在缺陷的。

(1)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存在的缺陷!!

(2)sem_timedwait函数介绍

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
  • 如果信号量大于0,则对信号量进行递减操作并立马返回正常
  • 如果信号量小于0,则阻塞等待,当阻塞超时时返回失败(errno 设置为 ETIMEDOUT)

第二个参数abs_timeout 参数指向一个指定绝对超时时刻的结构,这个结果由自 Epoch,1970-01-01 00:00:00 +0000(UTC)秒数和纳秒数构成。这个结构定义如下:

struct timespec {
   
    time_t tv_sec;        /* 秒 */
    long   tv_nsec;       /* 纳秒 */
};

2. 解决方法

可以通过 sem_trywait+ usleep 的方式来实现与 sem_timedwait 函数的类似功能,并且不会发生因系统时间往前改而出现一直阻塞的问题,下面来介绍一下sem_trywait函数。

(1)sem_trywait函数介绍

函数 sem_trywait() 和 sem_wait() 有一点不同,即如果信号量的当前值为0,则返回错误而不是阻塞调用。错误值errno设置为EAGAIN。sem_trywait()其实是sem_wait()的非阻塞版本

int sem_trywait(sem_t *sem)

执行成功返回0,执行失败返回 -1且信号量的值保持不变。

(2)sem_trywait + usleep的方式实现

主要实现的思路:

sem_trywait 函数不管信号量为0或不为0都会立刻返回。

  • 当函数正常返回的时候就不usleep;
  • 当函数不正常返回时就通过usleep来实现延时;

具体实现方式如下代码中的 bool Wait( size_t timeout ) 函数:

#include <string>
#include<iostream>

#include<semaphore.h>
#include <time.h>

sem_t g_sem;

// 获取自系统启动的调单递增的时间
inline uint64_t GetTimeConvSeconds( timespec* curTime, uint32_t factor )
{
   
    // CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
    clock_gettime( CLOCK_MONOTONIC, curTime );
    return static_cast<uint64_t>(curTime->tv_sec) * factor;
}

// 获取自系统启动的调单递增的时间 -- 转换单位为微秒
uint64_t GetMonnotonicTime()
{
   
    timespec curTime;
    uint64_t result = GetTimeConvSeconds( &curTime, 1000000 );
    result += static_cast<uint32_t>(curTime.tv_nsec) / 1000;
    return result;
}

// sem_trywait + usleep的方式实现
// 如果信号量大于0,则减少信号量并立马返回true
// 如果信号量小于0,则阻塞等待,当阻塞超时时返回false
bool Wait( size_t timeout )
{
   
    const size_t timeoutUs = timeout * 1000; // 延时时间由毫米转换为微秒
    const size_t maxTimeWait 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值