Linux 设置系统时钟对时序的影响

        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;
}
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值