Linux C语言定时任务的实现

1. 前情提要

最近需要编写一个小的测试程序,在while循环中读取数据并且不间断的读取10分钟,10分钟之后就不再读取数据。一开始是想用一个很大的数来计数,每次循环减一,减到0就退出while,但是这种方法不精准,而且这个数字也已经超出范围。后来就想定义一个flag,刚开始flag为true,while进入循环读取数据,等10分钟后flag被某个任务函数赋值为false,while循环就退出了。大致框架如下:

static bool flag = true;
void fun()
{flag = false;
}
int main()
{while(flag){//读取数据代码}
}

所以这里需要一个定时任务,一到时间就调用fun函数设置flag。在此,我使用了POSIX的定时器来达到我的目的。

2. POSIX相关函数介绍

posix相关的定时器函数包含在以下头文件中:

#include 
#include 

2.1 创建定时器

函数原型:

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

函数说明:创建一个POSIX标准的定时器
参数说明:
clock_id:系统时钟的宏,该参数表明了定时器是基于哪个系统时钟来创建的。常见的宏有以下:

#define CLOCK_REALTIME    0    
//表示从1970.1.1到目前系统时间,属于相对时间#define CLOCK_MONOTONIC   1
//单调的时间,也是绝对的时间,表示系统开启到目前的时间#define CLOCK_PROCESS_CPUTIME_ID  2
// 本进程到当前代码系统CPU花费的时间#define CLOCK_THREAD_CPUTIME_ID  3
//本线程到当前代码系统CPU花费的时间

除了以上宏,还有七种系统时钟的宏,这里就不一一介绍了,在time.h中可以查看。

evp:环境值,其结构体主要成员以下有如下:

struct sigevent
{sigval_t sigev_value;int sigev_signo;int sigev_notify;.....
}
union sigval_t
{int sival_int;void* sival_ptr;
}

sigev_notify表示定时器到期后需要采取的行为,它的取值有如下几种:

enum
{
SIGEV_SIGNAL = 0, // 设置该值时说明定时器到期时会产生信号,该信号由sigev_signo指定
SIGEV_NONE , // 设置该值防止定时器到期时产生信号
SIGEV_THREAD, //通过线程创建传递信号,这个不太确定
SIGEV_THREAD_ID //表示信号会发送到指定的线程
}

sigev_signo表示定时器到期时将会发出的信号。这些信号在signum.h中有定义。常用的信号由如下:

#define SIGALRM 		14 // 时钟信号
#define SIGUSR1			10 //用户自定义信号1
#define SIGUSR2 		12  //用户自定义信号2
...

还有最后一个成员是sigev_value,它则是来绑定定时器的。表示这些设置的环境将会作用到哪个定时器上。

该函数最后一个参数是timerid,表示定时器的id,定时器创建成功,将会产生一个id,而该id就会被赋值给timerid。

函数返回值:
返回0表示成功,返回-1表示失败。

2.2 初始化定时器

经过上述函数创建了一个定时器之后,还需要设置定时器的定时周期以及启动时钟周期(即过了多久开始启动时钟)。这项工作交由timer_settime 接口来完成,其函数原型如下:

int timer_settime (timer_t timerid, int flags,const struct itimerspec *value,struct itimerspec *old_value)

函数参数说明:
timer_id:定时器的ID,指定初始化的定时器,由timer_create函数产生。
flags:0表示相对时间,1表示绝对时间。
value:保存一个结构体的地址,该结构体就包含了定时周期以及启动周期。
结构体如下:

struct itimerspec{struct timespec it_interval;  //该值表示定时器启动后定时周期是多少struct timespec it_value;  //该值表示过多久开始启动定时器};

而结构体timespec则有两个成员,分别是秒和纳秒,如下:

struct timespec
{__time_t tv_sec;		/* Seconds.  */__syscall_slong_t tv_nsec;	/* Nanoseconds.  */
};

可见该定时器的精准度还是非常高的。
参数old_value通常情况下都是取0值或者NULL。

2.3 删除定时器
任务完成后,不需要定时器则可以使用下面的接口来删除定时器。
函数原型:

int timer_delete (timer_t __timerid)

函数只有一个参数,即定时器的ID,表明删除指定id的定时器。

好了,现在定时器有了,并且也可以设置定时器到期时产生的信号,现在就差信号产生时,怎么去触发执行指定的任务了。这就需要signal函数介入了。

2.4 signal函数
函数原型:

void (*signal(int sig, void (*func)(int)))(int)

函数说明:该函数是用于设置捕获到某一信号时所要采用的动作。
参数说明:
signum:指明了所要处理的信号类型,它可以取除SIGKILL和SIGSTOP之外的任意信号。
第二个参数则是一个函数指针,该函数无返回值,且包含一个int型的参数,表明了当产生信号时,函数指针指向的函数将会被调用。

3. 简单的小例子

接下来看一个简单的小例子来了解一下定时器的使用。该程序的功能就是在while中隔1s打印一个字符串,10s后退出while结束打印。

#include
#include
#include
#include
#include static bool flag = true;   
timer_t timeid;  //定义一个全局的定时器idvoid task(int i)  
{  printf("task start\n");flag = false;
}  void create_timer(){/****创建定时器***********/struct sigevent evp;  //环境结构体int ret = 0;memset(&evp, 0, sizeof(struct sigevent));evp.sigev_value.sival_ptr = &timeid;    //绑定i定时器evp.sigev_notify = SIGEV_SIGNAL;  //设置定时器到期后触发的行为是为发送信号evp.sigev_signo = SIGUSR1;  //设置信号为用户自定义信号1signal(SIGUSR1, task);  //绑定产生信号后调用的函数ret = timer_create(CLOCK_REALTIME, &evp, &timeid);  //创建定时器if( ret  == 0)   printf("timer_create ok\n");  } void init_timer()
{int ret = 0;struct itimerspec ts;  ts.it_interval.tv_sec = 1;  //设置定时器的定时周期是1sts.it_interval.tv_nsec = 0;  ts.it_value.tv_sec = 10;  //设置定时器10s后启动ts.it_value.tv_nsec = 0;  ret = timer_settime(timeid, 0, &ts, NULL);  //初始化定时器if( ret ==0)   printf("timer_settime ok\n");  
}
int main()  
{  create_timer();init_timer();while(flag){printf("ss\n");usleep(1000*1000);} 
} 

注意:编译程序时需要加上-lrt。

测试结果:

timer_create ok
timer_settime ok
ss
ss
ss
ss
ss
ss
ss
ss
ss
ss
task start

这样一来,while循环运行的时间就可以随意控制了。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部