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 <time.h>
#include <signal.h>

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<stdio.h>
#include<signal.h>
#include<time.h>
#include<string.h>
#include <unistd.h>

static bool flag = true;   
timer_t timeid;  //定义一个全局的定时器id
 
void 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;  //设置信号为用户自定义信号1
    signal(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;  //设置定时器的定时周期是1s
    ts.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循环运行的时间就可以随意控制了。

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐