sem_timedwait 和修改系统时间
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 |
从上面的表格可以看出,其实kernel是有多种timeout的:CLOCK_MONOTONIC和CLOCK_REALTIME. 但是到了glib2.30这个sem_timedwait才和它的说明手册的行为保持一致了。
那如果我还想用glibc2.19那种timeout的行为怎么办?两种方法:
把你的glibc降级到glibc2.19或者一下的版本。这个在linux的平台上应该就是
系统启动时间的时钟(
CLOCK_MONOTONIC)。
寻找时钟为
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 和修改系统时间相关推荐
- sem_timedwait_C/C++ 修改系统时间,导致sem_timedwait 一直阻塞的问题解决和分析
修改系统时间,导致sem_timedwait 一直阻塞的问题解决和分析 介绍 最近修复项目问题时,发现当系统时间往前修改后,会导致sem_timedwait函数一直阻塞.通过搜索了发现int sem_ ...
- linux下如何修改系统时间
我们一般使用"date -s"命令来修改系统时间.比如将系统时间设定成2018年2月23日的命令如下. #date -s 02/23/2018 将系统时间设定成下午11点12分0秒 ...
- VB 禁止修改系统时间
VB 禁止修改系统时间 添加到网络收藏宣传得积分可免费下载本站所有资源 VB 禁止修改系统时间 当任何程序或用户修改系统时间的时候,系统会将 WM_TIMECHANGE ...
- linux 修改时间的命令,Linux 常用命令(查看版本、修改系统时间)
1.查看内核版本:cat /proc/version [root@gjxb default]# cat /proc/version Linux version 2.6.9-78.8AXS2smp (p ...
- CentOS7手动修改系统时间
CentOS7 永久修改系统时间 安装在虚拟机上的CentOS7的时间分为系统时间和硬件时间.二者都修改,重启系统(init 6 )才会永久生效. 修改步骤如下 查看当前系统时间 date 修 ...
- 转:Android中如何修改系统时间(应用程序获得系统权限)
在 android 的API中有提供 SystemClock.setCurrentTimeMillis()函数来修改系统时间,可惜无论你怎么调用这个函数都是没用的,无论模拟器还是真机,在logcat中 ...
- 计算机系统时间无法更改,电脑时间不能修改|系统时间改不了 四个处理办法
电脑时间不能修改|系统时间改不了 四个处理办法? 电脑时间不但可以让网友准确的查看时间,还对软件的运行.工作等方面至关重要,有时大家看电脑时间不准确想要修改,确发现改不了系统时间.碰到该故障的朋友不要 ...
- windows下命令行修改系统时间;修改系统时间的软件
找了很久,都没有找到,还找了关键词 dos下修改系统时间 因为看到linux下修改系统时间是用hwclock 命令写入主板芯片. 而我由于某些原因想自动化修改系统时间,所以找windows下修改系统时 ...
- 经纬度距离,修改系统时间,读取ini文件
1. 大家都知道地球是椭圆形的,同时使用一个经度和纬度可以定义唯一的位置.下面是现实从两个经纬度计算它们之间的距离 using System; using System.Collections.Gen ...
最新文章
- R可视化包ggplot2设置轴范围
- linux 信号 core,Shell 信号发送与捕捉
- multiprocessing创建自定义进程类
- 8.22 13.1-13.3
- Jenkins + gitlab webhook实现自动化部署
- 直播预告丨 Oracle 12C~19C统计信息的最佳实践
- VirtualBox 安装 CentOS 7.6 操作记录
- 【理财】【学校财务信息管理系统】一卡通网络金融化
- php laravel实战项目,Laravel框架应用:7个实战项目
- python 实现图片转视频
- V神(Vitalik Buterin),区块链名人—以太坊(Eth)创始人简介
- 论文《Fast spatial–temporal stereo matching for 3Dface reconstruction under speckle pattern projection》
- MIPS指令 MIPS架构
- ArcGIS Server 10.8.1安装
- 【python】python读取命令行选项参数
- 计算机网络通信中的交换方式有哪几种,数据通信方式有哪几种
- linux系统四个组成部分,Linux系统由哪几部分组成?系统详解(干货)
- MySQL的二叉树、平衡二叉树、2,3查找树、B树、B+树索引
- pdf如何转换为word文档
- 徐汉彬:Web系统大规模并发——电商秒杀与抢购
热门文章
- 室内场景识别任务的参赛攻略!第一名经验分享,Malena.
- Python爬取暴走漫画动态图
- 详解Linux系统的开机、重启和用户登录注销
- C4D新手党使用小技巧——如何给对象着色以及添加材质或贴图?如何输出PNG格式?
- 搭建Spark开发环境并完成wordcount示例
- 大话备考之数据结构综合题
- 汽车电商“智变”时刻:“价格全知道”背后的超叠加“一刀”
- 通道趋势策略结合马丁的ea编程实践(九)附运行成果
- ir指令、立即数的作用_AI框架中图层IR的分析
- 医学影像坐标系问题(世界坐标系、解剖坐标系和图像坐标系)