sem_timedwait 和修改系统时间

        对于int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);传入的第二个阻塞时间参数是绝对的时间戳。原意是timeout时间是墙上时间,到达那个时间点如果还没有等到信号量,那就timeout.

如果有人修改了系统时间,那么这个API的行为可能就不是你想要的了。

但是在一些Linux的系统里面,它的时间最终却是按照系统启动时间的行为来工作的,这个好像就是很多人歪打正着的正和他们的用意。当然我刚开始也是这样理所当然的使用着,我的代码一直都是可以不受修改时间的影响的。

直到前些日子我们做新的项目时,glibc升级成了2.30的(原来的是2.19)。结果修改时间后,我的一些关于sem_timedwait代码逻辑就不对了: 有的要等很久才能timeout,如把时间改到过去了;有的就立刻timeout,如果时间改到未来了。

所以很大的可能是和glibc有关系了。于是仔细分析glibc不同版本的代码,我发现了如下的信息:

glibc: 2.19

glibc:  2.30

#include <errno.h>

#include <sysdep.h>

#include <lowlevellock.h>

#include <internaltypes.h>

#include <semaphore.h>

#include <pthreadP.h>

#include <shlib-compat.h>

extern void __sem_wait_cleanup (void *arg) attribute_hidden;

/* This is in a seperate function in order to make sure gcc

puts the call site into an exception region, and thus the

cleanups get properly run.  */

static int

__attribute__ ((noinline))

do_futex_timed_wait (struct new_sem *isem, struct timespec *rt)

{

int err, oldtype = __pthread_enable_asynccancel ();

err = lll_futex_timed_wait (&isem->value, 0, rt,

isem->private ^ FUTEX_PRIVATE_FLAG);//Paky: just use “CLOCK_MONOTONIC”!!!

__pthread_disable_asynccancel (oldtype);

return err;

}

int

sem_timedwait (sem_t *sem, const struct timespec *abstime)

{

struct new_sem *isem = (struct new_sem *) sem;

int err;

if (atomic_decrement_if_positive (&isem->value) > 0)

return 0;

if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)

{

__set_errno (EINVAL);

return -1;

}

atomic_increment (&isem->nwaiters);

pthread_cleanup_push (__sem_wait_cleanup, isem);

while (1)

{

struct timeval tv;

struct timespec rt;

int sec, nsec;

/* Get the current time.  */

__gettimeofday (&tv, NULL);

/* Compute relative timeout.  */

sec = abstime->tv_sec - tv.tv_sec;

nsec = abstime->tv_nsec - tv.tv_usec * 1000;

if (nsec < 0)

{

nsec += 1000000000;

--sec;

}

/* Already timed out?  */

if (sec < 0)

{

__set_errno (ETIMEDOUT);

err = -1;

break;

}

/* Do wait.  */

rt.tv_sec = sec;

rt.tv_nsec = nsec;

err = do_futex_timed_wait(isem, &rt);

if (err != 0 && err != -EWOULDBLOCK)

{

__set_errno (-err);

err = -1;

break;

}

if (atomic_decrement_if_positive (&isem->value) > 0)

{

err = 0;

break;

}

}

pthread_cleanup_pop (0);

atomic_decrement (&isem->nwaiters);

return err;

}

#include "sem_waitcommon.c"

/* This is in a separate file because because sem_timedwait is only provided

if __USE_XOPEN2K is defined.  */

int

sem_timedwait (sem_t *sem, const struct timespec *abstime)

{

if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)

{

__set_errno (EINVAL);

return -1;

}

/* Check sem_wait.c for a more detailed explanation why it is required.  */

__pthread_testcancel ();

if (__new_sem_wait_fast ((struct new_sem *) sem, 0) == 0)

return 0;

else

return __new_sem_wait_slow ((struct new_sem *) sem,

CLOCK_REALTIME, abstime);

}

2.19 对应的程序的strace log: 2.30 对应的程序的strace log:

pid 2414] 15:31:00.844765 futex(0x37188, FUTEX_WAIT_PRIVATE, 0, {1, 999572500}) = -1 ETIMEDOUT (Connection timed out) <1.999733>

[pid 3405] 03:40:45.437693 futex(0xb2c490, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, {tv_sec=1879405212750659647, tv_nsec=859005174888}, FUTEX_BITSET_MATCH_ANY
) = -1 ETIMEDOUT (Connection timed out) <2.000894>

从上面的表格可以看出,其实kernel是有多种timeout的:CLOCK_MONOTONIC和CLOCK_REALTIME. 但是到了glib2.30这个sem_timedwait才和它的说明手册的行为保持一致了。

   那如果我还想用glibc2.19那种timeout的行为怎么办?两种方法:

  1. 把你的glibc降级到glibc2.19或者一下的版本。这个在linux的平台上应该就是系统启动时间的时钟(CLOCK_MONOTONIC)。
  2. 寻找时钟为CLOCK_MONOTONIC的timeout的API。在新的glibc2.30里面已经有新的API了: 

int sem_clockwait (sem_t *sem, clockid_t clockid,const struct timespec *abstime) Behaves like sem_timedwait except the time abstime is measured against the clock specified by clockid rather than CLOCK_REALTIME. Currently, clockid must be either CLOCK_MONOTONIC or CLOCK_REALTIME.

截至目前这个问题应该是找到根本原因和解决方案了。所以我只需要根据当前的glibc的版本来决定我的代码要如何封装,比如通过编译选项来控制我们是否可以用新的API:sem_clockwait

#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 30
         //TRUE if the version of the GLib header files is the same as or newer than the passed-in version.
         if (clock_type == 1)
         {
             while ( ( status = sem_clockwait( semId, CLOCK_MONOTONIC, &expire ) ) != 0 && errno == EINTR );
       }
       else
       {
            while ( ( status = sem_clockwait( semId, CLOCK_REALTIME, &expire ) ) != 0 && errno == EINTR );
        }
         #else
         while ( ( status = sem_timedwait( semId, &expire ) ) != 0 && errno == EINTR );
         #endif

下面的代码是个简单的测试代码是我当时的一个测试用例:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <sys/time.h>#include <string>
#include <fstream>//# define CLOCK_REALTIME           0
/* Monotonic system-wide clock.  */
//# define CLOCK_MONOTONIC      1int GetTimeout(struct timespec *ts_caller, int clock_type, int delayTicks)
{if (ts_caller != NULL){long mswait;struct timespec ts_cur;//struct timespec   ts_delay;//mswait = (delayTicks*1000)/CLKTICKS_PER_SEC ;if (clock_type == 1)clock_gettime(CLOCK_MONOTONIC, &ts_cur);elseclock_gettime(CLOCK_REALTIME, &ts_cur);//ts_delay.tv_sec = (mswait / 1000 );//ts_delay.tv_nsec = ( mswait % 1000 ) * 1000 * 1000;//if( ts_cur.tv_nsec + ts_delay.tv_nsec > (1000*1000*1000) )///{//   ts_delay.tv_sec++;//   ts_delay.tv_nsec -= 1000*1000*1000;//}ts_caller->tv_sec = ts_cur.tv_sec + delayTicks;ts_caller->tv_nsec = ts_cur.tv_nsec + 0;return 0;}else{return -1;}
}int testSemWait(sem_t *semId, int clock_type, int timeout)
{int status = -1;struct timespec expire = {0, 0};if ((status = sem_trywait(semId)) != 0) //need waiting..{GetTimeout(&expire, clock_type, timeout);
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 30//TRUE if the version of the GLib header files is the same as or newer than the passed-in version.if (clock_type == 1){while ((status = sem_clockwait(semId, CLOCK_MONOTONIC, &expire)) != 0 && errno == EINTR);}else{while ((status = sem_clockwait(semId, CLOCK_REALTIME, &expire)) != 0 && errno == EINTR);}
#elsewhile ((status = sem_timedwait(semId, &expire)) != 0 && errno == EINTR);
#endif}return status;
}int main(int argc, char **argv)
{int clock_type = 0;int timeout = 30;sem_t m_sem;sem_init(&m_sem, 0, 0);if (argc >= 2)clock_type = atoi(argv[1]);if (argc >= 3)timeout = atoi(argv[2]);printf("clock type is %d (1 for CLOCK_MONOTONIC, other for CLOCK_REALTIME), timeout:%d\n", clock_type, timeout);std::string strTraceString;time_t timeT;struct tm stCurTime;char cDateTime[128] = {0};char cTid[64] = {0};struct timeval testTime;gettimeofday(&testTime, NULL);timeT = testTime.tv_sec;localtime_r(&timeT, &stCurTime);sprintf(cDateTime, "\r\n%d-%02d-%02d%s%02d:%02d:%02d.%06lu", stCurTime.tm_year + 1900, stCurTime.tm_mon + 1,stCurTime.tm_mday," ", stCurTime.tm_hour, stCurTime.tm_min, stCurTime.tm_sec, testTime.tv_usec);printf("start time:%s\n", cDateTime);testSemWait(&m_sem, clock_type, timeout);gettimeofday(&testTime, NULL);timeT = testTime.tv_sec;localtime_r(&timeT, &stCurTime);sprintf(cDateTime, "\r\n%d-%02d-%02d%s%02d:%02d:%02d.%06lu", stCurTime.tm_year + 1900, stCurTime.tm_mon + 1,stCurTime.tm_mday," ", stCurTime.tm_hour, stCurTime.tm_min, stCurTime.tm_sec, testTime.tv_usec);printf("end time:%s\n", cDateTime);
}

总结: 其实在软件升级的时候,我们经常会遇到一些看起来很奇怪的现象。但是这中间都是有根源了,就看你要不要打破沙锅追到底了。

sem_timedwait 和修改系统时间相关推荐

  1. sem_timedwait_C/C++ 修改系统时间,导致sem_timedwait 一直阻塞的问题解决和分析

    修改系统时间,导致sem_timedwait 一直阻塞的问题解决和分析 介绍 最近修复项目问题时,发现当系统时间往前修改后,会导致sem_timedwait函数一直阻塞.通过搜索了发现int sem_ ...

  2. linux下如何修改系统时间

    我们一般使用"date -s"命令来修改系统时间.比如将系统时间设定成2018年2月23日的命令如下. #date -s 02/23/2018 将系统时间设定成下午11点12分0秒 ...

  3. VB 禁止修改系统时间

    VB 禁止修改系统时间 添加到网络收藏宣传得积分可免费下载本站所有资源             VB 禁止修改系统时间   当任何程序或用户修改系统时间的时候,系统会将 WM_TIMECHANGE   ...

  4. linux 修改时间的命令,Linux 常用命令(查看版本、修改系统时间)

    1.查看内核版本:cat /proc/version [root@gjxb default]# cat /proc/version Linux version 2.6.9-78.8AXS2smp (p ...

  5. CentOS7手动修改系统时间

    CentOS7 永久修改系统时间 安装在虚拟机上的CentOS7的时间分为系统时间和硬件时间.二者都修改,重启系统(init 6 )才会永久生效. 修改步骤如下 查看当前系统时间 date     修 ...

  6. 转:Android中如何修改系统时间(应用程序获得系统权限)

    在 android 的API中有提供 SystemClock.setCurrentTimeMillis()函数来修改系统时间,可惜无论你怎么调用这个函数都是没用的,无论模拟器还是真机,在logcat中 ...

  7. 计算机系统时间无法更改,电脑时间不能修改|系统时间改不了 四个处理办法

    电脑时间不能修改|系统时间改不了 四个处理办法? 电脑时间不但可以让网友准确的查看时间,还对软件的运行.工作等方面至关重要,有时大家看电脑时间不准确想要修改,确发现改不了系统时间.碰到该故障的朋友不要 ...

  8. windows下命令行修改系统时间;修改系统时间的软件

    找了很久,都没有找到,还找了关键词 dos下修改系统时间 因为看到linux下修改系统时间是用hwclock 命令写入主板芯片. 而我由于某些原因想自动化修改系统时间,所以找windows下修改系统时 ...

  9. 经纬度距离,修改系统时间,读取ini文件

    1. 大家都知道地球是椭圆形的,同时使用一个经度和纬度可以定义唯一的位置.下面是现实从两个经纬度计算它们之间的距离 using System; using System.Collections.Gen ...

最新文章

  1. R可视化包ggplot2设置轴范围
  2. linux 信号 core,Shell 信号发送与捕捉
  3. multiprocessing创建自定义进程类
  4. 8.22 13.1-13.3
  5. Jenkins + gitlab webhook实现自动化部署
  6. 直播预告丨 Oracle 12C~19C统计信息的最佳实践
  7. VirtualBox 安装 CentOS 7.6 操作记录
  8. 【理财】【学校财务信息管理系统】一卡通网络金融化
  9. php laravel实战项目,Laravel框架应用:7个实战项目
  10. python 实现图片转视频
  11. V神(Vitalik Buterin),区块链名人—以太坊(Eth)创始人简介
  12. 论文《Fast spatial–temporal stereo matching for 3Dface reconstruction under speckle pattern projection》
  13. MIPS指令 MIPS架构
  14. ArcGIS Server 10.8.1安装
  15. 【python】python读取命令行选项参数
  16. 计算机网络通信中的交换方式有哪几种,数据通信方式有哪几种
  17. linux系统四个组成部分,Linux系统由哪几部分组成?系统详解(干货)
  18. MySQL的二叉树、平衡二叉树、2,3查找树、B树、B+树索引
  19. pdf如何转换为word文档
  20. 徐汉彬:Web系统大规模并发——电商秒杀与抢购

热门文章

  1. 室内场景识别任务的参赛攻略!第一名经验分享,Malena.
  2. Python爬取暴走漫画动态图
  3. 详解Linux系统的开机、重启和用户登录注销
  4. C4D新手党使用小技巧——如何给对象着色以及添加材质或贴图?如何输出PNG格式?
  5. 搭建Spark开发环境并完成wordcount示例
  6. 大话备考之数据结构综合题
  7. 汽车电商“智变”时刻:“价格全知道”背后的超叠加“一刀”
  8. 通道趋势策略结合马丁的ea编程实践(九)附运行成果
  9. ir指令、立即数的作用_AI框架中图层IR的分析
  10. 医学影像坐标系问题(世界坐标系、解剖坐标系和图像坐标系)