Linux 系统很多机制和系统实时时钟有很大的关系,它们基于系统实时时钟进行计时的调度,但是当调整系统时钟后,将对这些应用参数影响,特别是NTP,或其他网络授时,当本地终端晶体发生老化后,时间差异比较大,很容易产生比较大的跨度调整,所以在相关的函数应该考虑实时时钟对它的影响。
1,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; /* 纳秒 */
};
void semwaittest1(mSec)
{
struct timespec t;
struct timeval time;
gettimeofday(&time, NULL);
time.tv_usec += mSec * 1000; // 微秒 = 毫秒 * 1000
if(time.tv_usec >= 1000000) // 进位,1000 000 微秒 = 1 秒
{
time.tv_sec += time.tv_usec / 1000000;
time.tv_usec %= 1000000;
}
t.tv_sec = time.tv_sec;
t.tv_nsec = time.tv_usec * 1000;
while(1)
{
int semvalue = -1;
sem_getvalue(&sem, &semvalue);
// 如果sem 信号量值>0,则sem_timedwait 立即返回;如果sem 信号量值≤0,则 sem_timedwait 阻塞等待 TIMEOUT秒后再返回。
int ret = sem_timedwait(&sem, &t);
printf("over call sem_timedwait, ret = %d\n", ret);
if (ret == -1)
{
sem_getvalue(&sem, &semvalue);
return NULL;
}
}
}
sem_timedwait采用绝对时间进行等待,但是是相对于系统时间为准,从1970.1.1到目前的时间,更改系统时间会更改获取的值。回调系统时间将导致sem_timedwait 一直阻塞。
解决方法采用相对时间进行等待,函数为clock_gettime。
int clock_gettime(clockid_t clk_id, struct timespec* tp);clk_id : 检索和设置的clk_id指定的时钟时间。
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,
中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间
struct timespec
{
time_t tv_sec; /* 秒*/
long tv_nsec; /* 纳秒*/
};
具体实现如下:
void mysleepMs(unsigned int msec) //ms
{
struct timespec req = {0};
req.tv_sec = msec / 1000;
req.tv_nsec = (msec % 1000) * 1000000;
while(clock_nanosleep(CLOCK_MONOTONIC,0,&req, &req)< 0 && errno == EINTR)
{
;
}
}
size_t getClockTime(void)
{
struct timespec t;
size_t timeMsec = 0;
clock_gettime(CLOCK_MONOTONIC,&t);
timeMsec = t.tv_nsec / 1000000;
timeMsec += t.tv_sec * 1000;
return timeMsec;
}
void semwaittest2(mSec)
{
int ret = -1;
unsigned int waitCnk = 0;
size_t timeOutMsec = 0;
size_t currMsec = 0;
currMsec = getClockTime();
timeOutMsec = getClockTime() + mSec;
while(1)
{
int semvalue = -1;
sem_getvalue(&sem, &semvalue);
waitCnk = 0;
do{
if( sem_trywait(&sem ) == 0 )
{
ret = 0;
break;
}
if(waitCnk < 10) /***max sleep 10ms***/
{
waitCnk ++;
}
mysleepMs(waitCnk);
currMsec = getClockTime();
}while(currMsec < timeOutMsec)
printf("over call sem_timedwait, ret = %d\n", ret);
if (ret == -1)
{
sem_getvalue(&sem, &semvalue);
return NULL;
}
}
}
2,定时器的影响
采用CLOCK_MONOTONIC定时方式,而不是采用 CLOCK_REALTIME方式
timer_create(CLOCK_MONOTONIC, &stSigEvt, &posix_timer);
3,线程 pthread_cond_timedwait的影响
不要采用CLOCK_REALTIME的计时方式,而采用CLOCK_MONOTONIC,示列代码如下:
int threadSleep(unsigned int msec)
{
pthread_cond_t cond;
pthread_mutex_t mutex;
pthread_condattr_t cattr;
struct timeval now;
struct timespec waittime;
int ret = -1;
long nsec = 0;
pthread_mutex_init(&mutex, NULL);
pthread_condattr_init(&cattr);
pthread_condattr_setclock(&cattr, CLOCK_MONOTONIC);
pthread_cond_init(&cond, &cattr);
clock_gettime(CLOCK_MONOTONIC, &waittime);
nsec = waittime.tv_nsec + (msec * 1000) * 1000;
waittime.tv_sec += nsec / 1000000000;
waittime.tv_nsec = nsec % 1000000000;
pthread_mutex_lock(&mutex);
ret = pthread_cond_timedwait(&cond, &mutex, &waittime);
pthread_mutex_unlock(&mutex);
return ret;
}