linux定时器

本文详细介绍了Linux系统中的两种定时器:setitimer和timer_create。setitimer通过发送信号实现定时,可用于延时和定时任务,示例中展示了如何设置6秒后触发,之后每5秒发送SIGALRM信号。timer_create提供了更多灵活性,如选择不同的时钟ID和通知方式,可以通过创建线程或发送信号来处理定时事件。示例中分别展示了通过线程和信号启动定时器的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux系统中定时器有很多种,alarm, select, setitimer, timer_create等等,这里只简单的介绍两种定时器,setitimer和timer_create。

1.第一种setitimer函数实现定时器
函数原型如下:

/*
参数 which:(可选值如下)
	ITIMER_REAL:	以系统真实的时间来计算,它送出SIGALRM信号。
	ITIMER_VIRTUAL:以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。
	ITIMER_PROF:	以该进程在用户态下和内核态下所费的时间来计算。它送出SIGPROF信号。
参数 *new_value: 用于对定时器的时间进行配置,即配置什么时候触发定时器
参数 *old_value: 获取上一次配置的时间参数值
*/

/*参数 结构体 itimerval */
struct itimerval {
    struct timeval it_interval; /* 第一次触发定时器后,每次触发定时器周期时间值 */
    struct timeval it_value;    /* 第一次触发定时器时间值 */
};

struct timeval {
    time_t      tv_sec;         /* seconds */
    suseconds_t tv_usec;        /* microseconds */
}


int setitimer(int which, const struct itimerval *new_value,
                     struct itimerval *old_value);

settimer工作机制是,先对it_value倒计时,当it_value为零时触发信号。然后重置为it_interval。继续对it_value倒计时。一直这样循环下去。

基于此机制。setitimer既能够用来延时运行,也可定时运行。

假如it_value为0是不会触发信号的,所以要能触发信号,it_value得大于0;假设it_interval为零,仅仅会延时。不会定时(也就是说仅仅会触发一次信号)。

old_value參数,通经常使用不上。设置为NULL,它是用来存储上一次setitimer调用时设置的new_value值。

举例:使用setitimer函数实现 第一次6s后触发定时器,第一次后以5s的周期触发定时器发送SIGALRM信号。(注意,同一进程中SIGALRM信息会被覆盖,即如果在使用SIGALRM信号为定时器信号期间,若其他地方同一时间段内其他程序同样使用SIGALRM信号来实现其他功能则会覆盖先前设置好的signal(SIGALRM, timeout1))

#include <stdio.h>
#include <time.h>
#include <sys/time.h>

void timeout1(int sig)
{
	time_t times=0;
	printf("tick time:%ld\n", time(&times) );	
}

void settime_alarm(void)
{
    static struct itimerval tick;
    
    signal(SIGALRM, timeout1);
    memset(&tick, 0, sizeof(tick));
 
    //第一次调用时间
    tick.it_value.tv_sec = 6;
    tick.it_value.tv_usec = 0;
	
    //第一次调用后,每次调用间隔时间
    tick.it_interval.tv_sec = 5;
    tick.it_interval.tv_usec = 0;
 
    if(setitimer(ITIMER_REAL, &tick, NULL) < 0)
    {
        printf("Set timer failed!\n");
    }
}
int main(int argc, char *argv[])
{
	settime_alarm();
	while(1);
	return 0;
}

2.第二种timer_create函数实现定时器

2.1 timer_create函数原型如下:

int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)

成功创建返回0,失败返回-1,并更新错误码。参数中timerid 是创建成功的timer id,另两个参数则相对复杂:
参数 clock_id :(可选值如下)

CLOCK_REALTIME: 			系统保存的时间,比如当前是1010分,我们起了一个10min的定时器,5min后,我们将系统时间修改成1010分,定时器还会再过10min到时。
CLOCK_MONOTONIC:			Timer严格按照设定的时间定时,无法通过修改时间改变;
CLOCK_PROCESS_CPUTIME_ID:	计时器只记录当前进程所实际花费的时间;比如当前进程只能获得50%的 CPU 时间,为了让进程真正地运行 10 分钟,到1030 分Timer才 到期。
CLOCK_THREAD_CPUTIME_ID:	以线程为计时实体,当前进程中的某个线程真正地运行了一定时间才触发 Timer

参数Sigevent:

struct sigevent {
        int sigev_notify;                    
        int sigev_signo;            
        union sigval sigev_value;     
        void (*sigev_notify_function) (union sigval); 
        void *sigev_notify_attributes;          
        pid_t sigev_notify_thread_id;  
 };
SIGEV_NONE: 	到期时不产生通知;
SIGEV_SIGNAL:	到期时将给进程投递一个信号sigev_signo可以用来指定使用什么信号;
SIGEV_THREAD:	定时器到期时将启动新的线程进行处理;此种情况下需要设置 sigev_notify_function。当 Timer 到期时,将使用该函数作为入口启动一个线程来处理信号;sigev_value保存了传入 sigev_notify_funct的参数。sigev_notify_attributes 如果非
				空,则应该是一个指向 pthread_attr_t 的指针,用来设置线程的属性
				(比如 stack 大小,detach 状态等);
SIGEV_THREAD_ID:到期时将向指定线程发送信号,通常和 SIGEV_SIGNAL 联合使用,这样当 
				Timer 到期时,系统会向由 sigev_notify_thread_id 指定的线程发
				送信号,否则可能进程中的任意线程都可能收到该信号

2.2 启动定时器timer_settime函数
timer_settime函数原型如下:

int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value,
                  struct itimerspec * old_value);
 
struct itimespec{
    struct timespec it_interval;
    struct timespec it_value;  
 
};

struct timespec{
    time_t tv_sec;    //s
    long tv_nsec;    //ns
};

参数 timerid: 是调用timer_create函数成功创建的timerid
参数 flags: flags取值只有2个: 0 和 TIMER_ABSTIME。当 flags 为 0 时, new_value->it_value 表示希望timer首次到期时的时间与启动timer的时间间隔(例如,希望timer在2秒后到期);当flags为 TIMER_ABSTIME 时, new_value->it_value 表示希望timer首次到期的绝对时间(例如希望timer在01:23:45到期);如果new_value->it_value设定的绝对时间早于当前的绝对时间, 那么timer会立即到期;如果时钟 CLOCK_REALTIME 被调整了,那么timer的首次过期时间也会适当调整。
*参数 new_value : 配置定时器的时间参数
参数 * old_value : 获取上一次配置的定时器参数

2.3 删除定时器函数:

int timer_delete (timer_t timerid);

2.4 举例

#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>

void timeout1(int sig)
{
	time_t times=0;
	printf("tick time:%ld\n", time(&times) );	
}

void tm_timeout(union sigval value)
{
	printf("create time sifval:%X\n", value.sival_int);
}

void create_time_by_thread(void)
{
	static timer_t times;
	struct itimerspec ts;	//用于配置定时器时间  
	struct sigevent evp;
	
	evp.sigev_notify            = SIGEV_THREAD;
    evp.sigev_notify_function   = tm_timeout;
	evp.sigev_value.sival_int   = 0xFEDC;	//传递参数给 tm_timeout函数
    evp.sigev_notify_attributes = NULL;
	
	timer_create(CLOCK_REALTIME, &evp, &times);
	
	if(1)
	{
		//第一次调用后,每次调用间隔时间,若不需要周期性启动定时器可将 下面两个参数设置值为0
		ts.it_interval.tv_sec = 3;
		ts.it_interval.tv_nsec = 0;
		
		//第一次调用时间
		ts.it_value.tv_sec =7;
		ts.it_value.tv_nsec = 0;	
		
		if(0 != timer_settime(times, 0, &ts, NULL))
		{
			printf("create timer to start failed\n");
		}
	}
	else
	{
		struct timespec   now;					//获取linux系统时间 
		clock_gettime(CLOCK_REALTIME, &now);
		ts.it_value.tv_sec =now.tv_sec + 7;
		ts.it_value.tv_nsec = now.tv_nsec + 0;	
		
		if(0 != timer_settime(times, TIMER_ABSTIME, &ts, NULL))		//绝对时间启动定时器
		{
			printf("create timer to start failed\n");
		}
	}
}

void create_timer_by_signal(void)
{
	static timer_t times;
	struct itimerspec ts;	//用于配置定时器时间
    struct timespec   now;	//获取linux系统时间
	struct sigevent evp;
	
	evp.sigev_notify            = SIGEV_SIGNAL;
	evp.sigev_signo 			= SIGUSR1;
	evp.sigev_value.sival_ptr	= &times;
    evp.sigev_notify_attributes = NULL;
	timer_create(CLOCK_REALTIME, &evp, &times);
	
	/*第一次调用后,每次调用间隔时间*/
	ts.it_interval.tv_sec = 3;
	ts.it_interval.tv_nsec = 0;
	
	/*第一次调用时间*/
	ts.it_value.tv_sec =5;
	ts.it_value.tv_nsec = 0;	
	
	signal(SIGUSR1, timeout1);
	
	timer_settime(times, 0, &ts, NULL);	
}

int main(int argc, char *argv[])
{	
	create_time_by_thread();
	create_timer_by_signal();	
	while(1);
	return 0;
}

参考于:https://blog.youkuaiyun.com/coeus7/article/details/104176391

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值