在Linux应用层使用POSIX定时器

ref :

背景

系统中的一个模块需要频繁的获取系统时间,使用linux中内置的函数开销过大,因为需要的精度不是很高(毫秒级),索性用signal函数配合setitimer实现了个简易的全局时钟。

但是后来发现,SIGALRM的中断信号回终止sleep,因为sleep就是用SIGALRM信号量实现的,得另想方案。

这个替代方案就是POSIX中内置的定时器:timer_create()(创建)、timer_settime()(初始化)以及 timer_delete(销毁),将自己的时间信号处理函数用timer_create注册为SIGUSR2,这样就不会中断sleep了。

POSIX定时器

最强大的定时器接口来自POSIX时钟系列,其创建、初始化以及删除一个定时器的行动被分为三个不同的函数:timer_create()(创建定时器)、timer_settime()(初始化定时器)以及timer_delete(销毁它)。

创建一个定时器

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

进程可以通过调用timer_create()创建特定的定时器,定时器是每个进程自己的,不是在fork时继承的。

clock_id说明定时器是基于哪 个时钟的,*timerid装载的是被创建的定时器的ID。该函数创建了定时器,并将他的ID 放入timerid指向的位置中。

参数evp指定了定时器到期要产生的异步通知。

  • 如果evp为NULL,那么定时器到期会产生默认的信号,对 CLOCK_REALTIMER来说,默认信号就是SIGALRM。
  • 如果要产生除默认信号之外的其它信号,程序必须将 evp->sigev_signo设置为期望的信号码。struct sigevent 结构中的成员evp->sigev_notify说明了定时器到期时应该采取的行动。通常,这个成员的值为SIGEV_SIGNAL,这个值说明在 定时器到期时,会产生一个信号。程序可以将成员evp->sigev_notify设为SIGEV_NONE来防止定时器到期时产生信号。
  • 如果几个定时器产生了同一个信号,处理程序可以用 evp->sigev_value来区分是哪个定时器产生了信号。要实现这种功能,程序必须在为信号安装处理程序时,使用struct sigaction的成员sa_flags中的标志符SA_SIGINFO。

clock_id取值为以下:

CLOCK_REALTIME :Systemwide realtime clock.
CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.
CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer.
CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.
CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.
CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.

sigevent 的原型

struct sigevent
{
int sigev_notify; //notification type
int sigev_signo; //signal number
union sigval sigev_value; //signal valu
void (*sigev_notify_function)(union sigval)
pthread_attr_t *sigev_notify_attributes
}
union sigval
{
int sival_int; //integer valu
void *sival_ptr; //pointer value
}

通过将evp->sigev_notify设定为如下值来定制定时器到期后的行为:

  • SIGEV_NONE:什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息。
  • SIGEV_SIGNAL: 当定时器到期,内核会将sigev_signo所指定的信号传送给进程。在信号处理程序中,si_value会被设定会sigev_value。
  • SIGEV_THREAD: 当定时器到期,内核会(在此进程内)以sigev_notification_attributes为线程属性创建一个线程,并且让它执行sigev_notify_function,传入sigev_value作为为一个参数。

启动一个定时器

​ timer_create()所创建的定时器并未启动。要将它关联到一个到期时间以及启动时钟周期,可以使用timer_settime()。

int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue); 

struct itimerspec {
struct timespec it_interval; /* timer period */
struct timespec it_value; /* timer expiration */
};

如同settimer(),it_value用于指定当前的定时器到期时间。当定时器到期,it_value的值会被更新成it_interval 的值。如果it_interval的值为0,则定时器不是一个时间间隔定时器,一旦it_value到期就会回到未启动状态。timespec的结构提供 了纳秒级分辨率:

如果flags的值为TIMER_ABSTIME,则value所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)。这个经修改的行为可避免取得当前时间、计算“该时间”与“所期望的未来时间”的相对差额以及启动定时器期间造成竞争条件。

如果ovalue的值不是NULL,则之前的定时器到期时间会被存入其所提供的itimerspec。如果定时器之前处在未启动状态,则此结构的成员全都会被设定成0。

获得一个活动定时器的剩余时间

int timer_gettime(timer_t timerid,struct itimerspec *value);

取得一个定时器的超限运行次数

有可能一个定时器到期了,而同一定时器上一次到期时产生的信号还处于挂起状态。在这种情况下,其中的一个信号可能会丢失。这就是定时器超限。程序可 以通过调用timer_getoverrun来确定一个特定的定时器出现这种超限的次数。定时器超限只能发生在同一个定时器产生的信号上。由多个定时器, 甚至是那些使用相同的时钟和信号的定时器,所产生的信号都会排队而不会丢失。

int timer_getoverrun(timer_t timerid);

执行成功时,timer_getoverrun()会返回定时器初次到期与通知进程(例如通过信号)定时器已到期之间额外发生的定时器到期次数。

举例来说,在我们之前的例子中,一个1ms的定时器运行了10ms,则此调用会返回9。如果超限运行的次数等于或大于DELAYTIMER_MAX,则此调用会 返回DELAYTIMER_MAX。

执行失败时,此函数会返回-1并将errno设定会EINVAL,这个唯一的错误情况代表timerid指定了无效的定时器。

删除一个定时器

int timer_delete (timer_t timerid);

一次成功的timer_delete()调用会销毁关联到timerid的定时器并且返回0。

执行失败时,此调用会返回-1并将errno设定会 EINVAL,这个唯一的错误情况代表timerid不是一个有效的定时器。

例子

例1

程序运行3秒以后,每隔1秒打印当前时间。

// test1.c
#include <signal.h>
#include <time.h>
#include <stdio.h>
void handle()
{
time_t t;
char p[32];
time(&t);
strftime(p, sizeof(p), "%T", localtime(&t));
printf("time is %s\n", p);
}
int main()
{
struct sigevent evp;
struct itimerspec ts;
timer_t timer;
int ret;
evp.sigev_value.sival_ptr = &timer;
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGUSR1;
signal(SIGUSR1, handle);
ret = timer_create(CLOCK_REALTIME, &evp, &timer);
if( ret )
perror("timer_create");
ts.it_interval.tv_sec = 1;
ts.it_interval.tv_nsec = 0;
ts.it_value.tv_sec = 3;
ts.it_value.tv_nsec = 0;
ret = timer_settime(timer, 0, &ts, NULL);
if( ret )
perror("timer_settime");
while(1);
}

编译命令:gcc test1.c -lrt

例2

程序运行3秒以后,每隔1秒打印当前时间。同时发送指定的信号。

// test2.c
#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h> void handle(union sigval v)
{
time_t t;
char p[32];
time(&t);
strftime(p, sizeof(p), "%T", localtime(&t));
printf("%s thread %lu, val = %d, signal captured.\n", p, pthread_self(), v.sival_int);
return;
} int main()
{
struct sigevent evp;
struct itimerspec ts;
timer_t timer;
int ret;
memset (&evp, 0, sizeof (evp));
evp.sigev_value.sival_ptr = &timer;
evp.sigev_notify = SIGEV_THREAD;
evp.sigev_notify_function = handle;
evp.sigev_value.sival_int = 3; //作为handle()的参数
ret = timer_create(CLOCK_REALTIME, &evp, &timer);
if( ret)
perror("timer_create");
ts.it_interval.tv_sec = 1;
ts.it_interval.tv_nsec = 0;
ts.it_value.tv_sec = 3;
ts.it_value.tv_nsec = 0;
ret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL);
if( ret )
perror("timer_settime");
while(1);
}

编译命令:gcc test2.c -lrt

在Linux应用层使用POSIX定时器的更多相关文章

  1. Linux应用层的定时器Timer使用详解【转】

    转自:http://blog.csdn.net/wwwtovvv/article/details/8601528 版权声明:本文为博主原创文章,未经博主允许不得转载. linux下定时器的使用 -- ...

  2. Linux 应用层的时间编程【转】

    转自:https://blog.csdn.net/chinalj2009/article/details/21223681 浅析 Linux 中的时间编程和实现原理,第 1 部分: Linux 应用层 ...

  3. 浅析 Linux 中的时间编程和实现原理一—— Linux 应用层的时间编程【转】

    本文转载自:http://www.cnblogs.com/qingchen1984/p/7007631.html 本篇文章主要介绍了"浅析 Linux 中的时间编程和实现原理一—— Linu ...

  4. Linux应用层直接操作GPIO

    Linux应用层直接操作GPIO 在一个老手的指导下,应用层可以直接操作GPIO,具体指设置GPIO的输入输出以及输出电平高或者低.这个大大地提高了灵活性,官方的文档有GPIO Sysfs Inter ...

  5. Linux 进程间通信(posix消息队列 简单)实例

    Linux 进程间通信(posix消息队列 简单)实例 详情见: http://www.linuxidc.com/Linux/2011-10/44828.htm 编译: gcc -o consumer ...

  6. 浅析linux内核中timer定时器的生成和sofirq软中断调用流程(转自http://blog.chinaunix.net/uid-20564848-id-73480.html)

    浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...

  7. 浅析linux内核中timer定时器的生成和sofirq软中断调用流程【转】

    转自:http://blog.chinaunix.net/uid-20564848-id-73480.html 浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_time ...

  8. 基于Linux应用层的6LOWPAN物联网网关及实现方法

    本发明涉及一种基于Linux应用层的6LOWPAN物联网网关及实现方法,所述物联网网关包括开发平台以及无线射频模块,其实现方法是:所述6LOWPAN物联网网关的以太网网口收到访问6LOWPAN无线传感 ...

  9. Linux设备驱动——内核定时器

    内核定时器使用 内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于 <Linux/timer.h> 和 kernel/timer.c 文件 ...

  10. Linux进程同步之POSIX信号量

    POSIX信号量是属于POSIX标准系统接口定义的实时扩展部分.在SUS(Single UNIX Specification)单一规范中,定义的XSI IPC中也同样定义了人们通常称为System V ...

随机推荐

  1. js实现懒加载原理

    概念:对于页面有很多静态资源的情况下(比如网商购物页面),为了节省用户流量和提高页面性能,可以在用户浏览到当前资源的时候,再对资源进行请求和加载.原理:当图片元素的偏移高度<=设备高度+滚动条与 ...

  2. Python使用HTMLTestRunner运行所有用例并产生报告

    #coding:utf-8import unittestimport osimport sysimport HTMLTestRunnercase_path = os.path.join(os.path ...

  3. Unity 热更--AssetBundle学习笔记 0.7

    AssetBundle AB包是什么? AssetBundle又称AB包,是Unity提供的一种用于存储资源的资源压缩包. Unity中的AssetBundle系统是对资源管理的一种扩展,通过将资源分 ...

  4. ide构建SpringMVC框架

    框架原理图如下: 1. 创建如图项目 2. 在lib中所需导入jar包 3. 配置变量 (1) (2)add library (3)选择web app libraries 4. 配置web.xml文件 ...

  5. 一键自动化博客发布工具,用过的人都说好(infoq篇)

    infoq的博客发布界面也是非常简洁的.首页就只有基本的标题,内容和封面图片,所以infoq的实现也相对比较简单. 一起来看看吧. 前提条件 前提条件当然是先下载 blog-auto-publishi ...

  6. C语言:如何删除在可视化网页中未可见的内容(网页txt)

    我这个代码仅仅限制于在chrome浏览器中下载china daliy的网页中实现删除可视化内容,因为每个网页的超链接或者文本主内容分布不一样,但是学会了删除一个网页类型的不可视化内容之后,修改其他网页 ...

  7. 【阿里天池云-龙珠计划】薄书的机器学习笔记——K近邻(k-nearest neighbors)初探Task02

    [阿里天池云-龙珠计划]薄书的机器学习笔记--K近邻(k-nearest neighbors)初探Task02 [给各位看官请安] 大家一起来集齐七龙珠召唤神龙吧!!! 学习地址:AI训练营机器学习- ...

  8. CH57x/CH58x/CH59x获取从机广播信息

    有时需要通过主机设备(MCU非手机)获取从设备的广播信息例如广播包,MAC地址,扫描应答包等 以下的程序片段及功能实现是在WCH的CH59X的observer例程上实现的: 1.获取广播包 所有的函数 ...

  9. 解决TrueNAS中Smb共享文件路径不区分大小写的问题

    问题 在Truenas中, 默认的smb文件分享中, 文件夹是不区分大小写的. 这在一些情况下会导致无法重命名等问题, 严重时可能会造成拷贝文件时的全文件夹文件丢失. 这是linux下的情况, 在已存 ...

  10. 自动化搭建专属 AI 绘图服务

    通义万相AIGC技术已经比较成熟,结合阿里云的计算和存储产品可以方便的搭建自己专属的 AI 绘图服务.例如<创意加速器:AI 绘画创作>这个解决方案,利用阿里自研的通义万相AIGC技术在  ...