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循环运行的时间就可以随意控制了。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!