Linux定时器

2023-12-13 10:40:33

setitimer实现定时器

通过itimerval结构体以及setitimer函数产生的信号,系统随之使用signal信号处理函数来处理产生的定时信号,从而实现定时器。

itimerval结构体

struct itimerval
{
    struct timeval it_interval;
    struct timeval it_value;
};

//it_interval:定时器周期,多久触发一次定时器中断
//it_value:程序跑到这之后,多久启动定时器

struct timeval
{
    _time_t tv_sec;         
    _suseconds_t tv_usec; 
};

?setitimer函数

int setitimer ( _itimer_which_t _which,
                const struct itimerval * _restrict _new,
                struct itimerval * _restrict _old)

setitimer()将value指向的结构体设为计时器的当前值,如果第三个参数oldvalue不是NULL(一般设为NULL),将返回计时器原有值。

参数说明

which:
  • ITIMER_REAL /数值为0,计时器的值实时递减,发送的信号是SIGALRM。
  • ITIMER_VIRTUAL /数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
  • ITIMER_PROF /数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。
_restrict _new

? ? ? ?初始化完毕的itimerval结构体地址

_restrict _old

????????通常设置为NULL,它是用来存储上一次setitimer调用时设置的_restrict _new值。

中断处理

与学习51一类单片机的中断类似,设置完定时器后,定时器到达时间后发出中断请求,cpu响应去执行中断处理函数。在linux中,中断请求类似于一个信号,使用signal(SIGALRM,signal_handler);捕捉中断信号SIGALRM,执行中断处理函数signal_handler。成功执行signal时,返回0;失败返回-1。

示例

//产生0.5ms周期的定时器中断,实现每1s打印一次hello
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>

static int i;
void signal_handler(int signum)
{
    i++;
    if(i==2000)    // 1 se = 2 * 1000 * 500 usec
    {
        printf("hello\n");
        i=0;

    }
}

int main()
{
    struct itimerval itv;
    itv.it_interval.tv_sec=0;
    itv.it_interval.tv_usec=500;

    itv.it_value.tv_sec=1;
    itv.it_value.tv_usec=0;

    if(setitimer(ITIMER_REAL,&itv,NULL)==-1)
    {
        perror("error");
        exit(-1);
    }

    signal(SIGALRM,signal_handler);
    while(1);
    return 0;
}
~  

timer_create实现定时器

timer_create创建定时器

函数原型

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

返回值

成功创建返回0,失败返回-1,并更新错误码。

参数说明

clock_id:
  • CLOCK_REALTIME:系统保存的时间,比如当前是10点10分,我们起了一个10min的定时器,5min后,我们将系统时间修改成10点10分,定时器还会再过10min到时。
  • CLOCK_MONOTONIC:imer严格按照设定的时间定时,无法通过修改时间改变;
  • CLOCK_PROCESS_CPUTIME_ID:计时器只记录当前进程所实际花费的时间;比如当前进程只能获得50%的 CPU 时间,为了让进程真正地运行 10 分钟,到10 点 30 分Timer才 到期。
  • CLOCK_THREAD_CPUTIME_ID:以线程为计时实体,当前进程中的某个线程真正地运行了一定时间才触发 Timer
evp:
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 指定的线程发送信号,否则可能进程中的任意线程都可能收到该信号
timerid:

????????创建成功的timer id。

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 :

????????获取上一次配置的定时器参数

timer_delete删除定时器函数

函数原型

int timer_delete (timer_t timerid);

示例

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


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

void timeout1(int sig)
{
	time_t times=0;			 //获取到当前时间
	printf("tick time:%ld\n", time(&times) );	
}


void create_time_by_thread(void)
{
	static timer_t times;	//time id
	
	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))	//3s调用一次tm_timeout
		{
			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;
}

?注:编译时记得要链接库

gcc xxx.c -lrt//编译链接库   实时库(real time):shm_open系列


?

文章来源:https://blog.csdn.net/m0_58985552/article/details/134838098
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。