[Linux用户空间编程-3]:Linux定时机制的几种实现方法

2023-11-01

作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客

本文网址:https://blog.csdn.net/HiWangWenBing/article/details/123376014


目录

前言:

定时器的常见使用方法

1. 使用sleep()和usleep()

2. 使用信号量SIGALRM + alarm()

3. 利用POSIX定时器:time.h

4. 使用RTC hardware device(适合实时性要求高的场合)

5. 使用select()


前言:

定时器Timer应用场景非常广泛,在Linux下,有以下几种方法:

(1)sleep

(2)SIGALRM + alarm()

(3)POSIX time

(4)使用RTC hardware device

(5)select

定时器的常见使用方法

1. 使用sleep()和usleep()

其中sleep精度是1秒,usleep精度是1微妙

使用这种方法缺点比较明显,在Linux系统中,sleep类函数不能保证精度,尤其在系统负载比较大时,sleep一般都会有超时现象。

while(1)

{

        sleep(1);

}

周期性的被调度器唤醒,调度执行。

2. 使用信号量SIGALRM + alarm()

这种方式的精度能达到1秒,其中利用了Linux系统的信号量机制,

首先注册信号量SIGALRM处理函数,调用alarm(),设置定时长度,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

#include <stdio.h>

#include <signal.h>

void timer_handler(int sig)

{

    if(SIGALRM == sig)

    {

        printf("timer\n");

        alarm(1); //再次发送alarm, 1表示1s后发送alarm

    }

    return ;

}

int main()

{

    signal(SIGALRM, timer_handler); //relate the signal and function

    alarm(1);    //trigger the timer_handler, 1表示发送alarm的延时时间

    getchar();

    return 0;

}

alarm方式虽然很好,但是无法首先低于1秒的精度。

3. 利用POSIX定时器:time.h

(1)概述

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

  • timer_create():创建定时器
  • timer_settime():初始化定时器
  • timer_delete:    销毁定时器

(2)代码示例

#include <stdio.h>  
#include <signal.h>  
#include <time.h>  
#include <string.h>  
#include <stdlib.h>  
#include <unistd.h>  
  
# 定时回调函数,函数会被定时中断周期的调用
void timer_thread(union sigval v)  
{  
    # v.sival_int从定时器软中断/Signal传递过来的信息
    printf("timer_thread function! %d\n", v.sival_int);  
}  
  
int main()  
{  
    # 定时器标识
    timer_t timerid;  

    # 定时软中断信息
    struct sigevent evp;  
    //清零初始化
    memset(&evp, 0, sizeof(struct sigevent));   
    
    # 定时器的创建者传递给定时器的处理者的信息:
    evp.sigev_value.sival_int = 111;
    # 定时线程被通知的方式,启动一个新的线程来调用callback函数
    evp.sigev_notify = SIGEV_THREAD;   
    # 定时回调函数
    evp.sigev_notify_function = timer_thread;        
  
     # 创建定时器
    // int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid);  
    // clockid:定时器的类型:
    // CLOCK_REALTIME,CLOCK_MONOTONIC,CLOCK_PROCESS_CPUTIME_ID,CLOCK_THREAD_CPUTIME_ID  
    // evp--存放环境值的地址,结构成员说明了定时器到期的通知方式和处理方式等  
    // timerid--用户存放返回的定时器标识符  
    
    if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1)  
    {  
        perror("fail to timer_create");  
        exit(-1);  
    }  
  
     # 设置定时器的参数:
    // XXX int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value,struct itimerspec *old_value);  
    // timerid--定时器标识  
    // flags--0表示相对时间,1表示绝对时间  
    // new_value--定时器的新初始值和间隔,如下面的it  
    // old_value--取值通常为0,即第四个参数常为NULL,若不为NULL,则返回定时器的前一个值  
      
    //第一次间隔it.it_value这么长,以后每次都是it.it_interval这么长,就是说it.it_value变0的时候会装载it.it_interval的值  
    struct itimerspec it;  
    it.it_interval.tv_sec = 1;  
    it.it_interval.tv_nsec = 0;  
    it.it_value.tv_sec = 1;  
    it.it_value.tv_nsec = 0;  
  
    if (timer_settime(timerid, 0, &it, NULL) == -1)  
    {  
        perror("fail to timer_settime");  
        exit(-1);  
    }  
  
    pause();  
  
    return 0;  
}  

# 获得定时器当期的计数值
/* 
 * int timer_gettime(timer_t timerid, struct itimerspec *curr_value); 
 * 获取timerid指定的定时器的值,填入curr_value 
 * 
 */  

(3)CLOCK_MONOTONIC与CLOCK_REALTIME的区别:

CLOCK_MONOTONIC:是monotonic time,monotonic time字面意思是单调时间,实际上它指的是系统启动以后流逝的时间,这是由变量jiffies来记录的。系统每次启动时jiffies初始化为0,每来一个timer interrupt,jiffies加1,也就是说它代表系统启动后流逝的tick数。jiffies一定是单调递增的。

CLOCK_REALTIME:是wall time,wall time字面意思是挂钟时间,实际上就是指的是现实的时间,这是由变量xtime来记录的。系统每次启动时将CMOS上的RTC时间读入xtime,这个值是"自1970-01-01起经历的秒数、本秒中经历的纳秒数",每来一个timer interrupt,也需要去更新xtime。
wall time不一定是单调递增的。因为wall time是指现实中的实际时间,如果系统要与网络中某个节点时间同步、或者由系统管理员觉得这个wall time与现实时间不一致,有可能任意的改变这个wall time。最简单的例子是,我们用户可以去任意修改系统时间,这个被修改的时间应该就是wall time,即xtime,它甚至可以被写入RTC而永久保存。一些应用软件可能就是用到了这个wall time,比如以前用vmware workstation,一启动提示试用期已过,但是只要把系统时间调整一下提前一年,再启动就不会有提示了,这很可能就是因为它启动时用gettimeofday去读wall time,然后判断是否过期,只要将wall time改一下,就可以欺骗过去了。

因此,如果需要严格的不受系统时间影响的定时器,则需要使用CLOCK_MONOTONIC类型。

4. 使用RTC hardware device(适合实时性要求高的场合)

RTC机制利用系统硬件中断提供的Real Time Clock机制,

通过读取RTC硬件/dev/rtc,通过ioctl()设置RTC频率,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

#include <stdio.h>

#include <linux/rtc.h>

#include <sys/ioctl.h>

#include <sys/time.h>

#include <sys/types.h>

#include <fcntl.h>

#include <unistd.h>

#include <errno.h>

#include <stdlib.h>

int main(int argc, char* argv[])

{

    unsigned long i = 0;

    unsigned long data = 0;

    int retval = 0;

    int fd = open ("/dev/rtc", O_RDONLY);

    if(fd < 0)

    {

        perror("open");

        exit(errno);

    }

    /*Set the freq as 4Hz*/

    if(ioctl(fd, RTC_IRQP_SET, 1) < 0)

    {

        perror("ioctl(RTC_IRQP_SET)");

        close(fd);

        exit(errno);

    }

    /* Enable periodic interrupts */

    if(ioctl(fd, RTC_PIE_ON, 0) < 0) 

    {

        perror("ioctl(RTC_PIE_ON)");

        close(fd);

        exit(errno);

    }

    while(1)

    {

          # 周期性的被硬件唤醒

          # 平时被阻塞在内核设备的同步锁上。

        if(read(fd, &data, sizeof(unsigned long)) < 0)

        {

                # 出错处理

            perror("read");

            close(fd);

            exit(errno);

        }

        printf("timer\n");

    }

    /* Disable periodic interrupts */

    ioctl(fd, RTC_PIE_OFF, 0);

    close(fd);

    return 0;

}

优点:这种方式比较方便,利用了系统硬件提供的RTC,精度可调,而且非常高。

缺点:

(1)需要在内核空间写内核驱动程序,并虚拟出一个硬件设备。

(2)用户空间程序通过read(fd, &data, sizeof(unsigned long)阻塞在该设备上。

(3)内核中断服务程序周期性唤醒阻塞read上上的用户空间程序。

由于该应用程序是有内核中断服务程序唤醒的,因此实时性比较高。

5. 使用select()

这种方法比较冷门,通过使用select(),来设置定时器;

原理利用select()方法的第5个参数,第一个参数设置为0,三个文件描述符集都设置为NULL,第5个参数为时间结构体,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

#include <sys/time.h>

#include <sys/select.h>

#include <time.h>

#include <stdio.h>

/*seconds: the seconds; mseconds: the micro seconds*/

void setTimer(int seconds, int mseconds)

{

    struct timeval temp;

    temp.tv_sec = seconds;

    temp.tv_usec = mseconds;

              # select被一直阻塞,直到超时后返回,起到了定时的作用。

    select(0, NULL, NULL, NULL, &temp);

    printf("timer\n");

    return ;

}

int main()

{

    int i;

    for(i = 0 ; i < 100; i++)

        setTimer(1, 0);

    return 0;

}

这种方法精度能够达到微妙级别,网上有很多基于select()的多线程定时器,说明select()稳定性还是非常好。

总结:如果对系统要求比较低,可以考虑使用简单的sleep(),毕竟一行代码就能解决;

如果系统对精度要求比较高,则可以考虑RTC机制和select()机制。


作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客

本文网址:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[Linux用户空间编程-3]:Linux定时机制的几种实现方法 的相关文章

  • 如何在perl中使用O_ASYNC和fcntl?

    我想使用 O ASYNC 选项 当管道可以读取时 SIGIO 的处理程序将运行 但以下代码不起作用 任何人都可以帮助我吗 bin env perl use Fcntl SIG IO sub print catch SIGIO n my fl
  • Grep 递归和计数

    需要在具有大量子目录的目录中搜索文件内的字符串 我在用着 grep c r string here 我怎样才能找到总数量 如何仅输出至少具有一个实例的文件 使用 Bash 的进程替换 这给出了我认为是您想要的输出 如果不是 请澄清问题 gr
  • php_network_getaddresses: getaddrinfo 失败: 名称或服务未知 (0) 连接失败..!

    我正在使用 php 邮件程序功能 但出现以下错误 如何修复它 2016 01 22 06 15 48 SMTP 错误 无法连接到服务器 php network getaddresses getaddrinfo失败 名称或服务未知 0 连接失
  • 用于获取特定用户 ID 和进程数的 Bash 脚本

    我需要 bash 脚本来计算特定用户或所有用户的进程 我们可以输入 0 1 或更多参数 例如 myScript sh root deamon 应该像这样执行 root 92 deamon 8 2 users has total proces
  • 如何回忆上一个 bash 命令的参数?

    Bash 有没有办法回忆上一个命令的参数 我通常这样做vi file c其次是gcc file c Bash 有没有办法回忆上一个命令的参数 您可以使用 or 调用上一个命令的最后一个参数 Also Alt can be used to r
  • 查找并删除超过 x 天的文件或文件夹

    我想删除超过 7 天的文件和文件夹 所以我尝试了 17 07 14 email protected cdn cgi l email protection find tmp mindepth 1 maxdepth 1 ctime 7 exec
  • 使用 --prof 选项创建多个日志文件而不是一个 v8.log 的节点

    我正在尝试使用 prof 选项来分析我的 Node 应用程序 但我发现不是一个单一的 v8 log 文件 而是使用诸如isolate 0x9582b40 v8 log isolate 0xa1cab78 v8 6049 等前缀创建的多个文件
  • 从哪里获取 iostream.h

    我正在尝试在 Linux 中做一些事情 但它抱怨找不到 iostream h 我需要安装什么才能获取此文件 这个标准头的正确名称是iostream没有扩展名 如果您的编译器仍然找不到它 请尝试以下操作 find usr include na
  • 命名互斥体的 Mono 替代方案

    在 Windows NET 上 命名的互斥体可用于同步多个进程 不幸的是 Mono 在 Linux 上不太支持这一点 他们的发行说明 http www mono project com Release Notes Mono 2 8 Shar
  • 无法连接到 Azure Ubuntu VM - 公钥被拒绝

    我们在 Azure 上使用 Ubuntu VM 一段时间了 很少遇到任何问题 然而 其中一台虚拟机最近出现了问题 出乎意料的是 Ubuntu VM 开始拒绝公钥 ssh i azure key email protected cdn cgi
  • 如何在 Linux 中向热敏打印机发送 ESC/POS 命令

    我正在尝试在热敏打印机上发送 ESC POS 命令 但每当我发送它们时 热敏打印机都会将它们打印为文本 而不是作为命令执行它们 我在 prn 文件中编写这些命令 每当我执行 lp 命令来打印文件时 这些 prn 文件也会被打印 但作为文本
  • 如何将命令输出作为多个参数传递给另一个命令

    我想将命令的每个输出作为多个参数传递给第二个命令 例如 grep pattern input returns file1 file2 file3 我想复制这些输出 例如 cp file1 file1 bac cp file2 file2 b
  • 安装 JDK 时出错:keytool 命令需要已安装的 proc fs (/proc)。 Linux 的 Windows 子系统

    我尝试在 Linux 的 Windows 子系统 Ubuntu 14 04 上安装 Oracle JDK 1 7 但出现以下错误 the keytool command requires a mounted proc fs proc Jav
  • grep 彩色线条

    我编写了一个简单的 PHP shell 脚本 它解析文件并输出某些元素 它产生大量的输出 采用不同的 bash 颜色 绿色表示正常 黄色表示警告 红色表示错误等 在开发过程中我想过滤掉一些行 例如 所有包含红色文本的行 我可以使用grep
  • 用于 e NetworkManager VPN 连接的 dbus 信号处理程序

    我需要开发一些在建立 VPN 连接时执行的 python 代码 VPN 由 NetworkManager 控制 我试图弄清楚如何为此使用 NM DBUS 事件 使用 dbus monitor system 我能够识别连接信号 signal
  • 使用 Python 将阿拉伯语或任何从右到左书写系统的字符串打印到 Linux 终端

    非常简单的例子是 city print city 我期望输出是 但实际上输出是相反的字符串 字母看起来有点不同 因为它们有开始 中间和结束形式 我无法将其粘贴到此处 因为复制粘贴会再次更正字符串的顺序 如何在 Linux 终端上正确打印阿拉
  • 在 Windows / Linux 中创建 Mac 包

    我自己努力制作一个 r 包 我按照 stackoverflow 中上一个问题的说明进行操作如何为外行开发软件包 http cran r project org bin windows Rtools 以下是我根据上一个问题采取的步骤 在新的
  • SIGHUP 用于重新加载配置

    根据signal 7 SIGHUP用于检测控制终端的挂起或控制进程的死亡 然而 我遇到过很多 OSS 守护进程 服务 其中SIGHUP用于启动配置的重新加载 这里有一些例子 hostapd sshd snort etc 这是实现重新加载的标
  • tar.gz 和 tgz 是同一个东西吗? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我创建了 tgz 文件tar czvf filecommand then 我最终得到了一个 tgz 文件 我想知道它和tar gz 之间的
  • ARM 的内核 Oops 页面错误错误代码

    Oops 之后的错误代码给出了有关 ARM EX 中的恐慌的信息 Oops 17 1 PREEMPT SMP在这种情况下 17 给出了信息 在 x86 中它代表 bit 0 0 no page found 1 protection faul

随机推荐

  • study

    学习的博客地址 1 对自己python有帮助过的博客 http blog csdn net anbo724 article category 831447 另外有hadoop 等其他分类
  • 机器学习--人脸自动补齐(11)

    随机树ExtraTreeRegressor 分列点随机选取 不考虑信息增益 减少过拟合 获取一个对象所属的类名称 model class name import numpy as np import pandas as pd import
  • 肖臻老师区块链公开课笔记

    前段时间 区块链大火 出现了很多种基于区块链技术的政务应用 之前通过零散的网页信息和讲座 自我感觉理解了block chain原理 当看到各种区块链技术广泛应用时 自己以技术理解 反而对之不屑 当然 也怀疑自己还没有理解了区块链 带着这种好
  • 二、Flink使用异步算子请求高德地图获取位置信息

    目录 Flink异步算子使用介绍 Flink使用异步算子请求高德地图获取位置信息代码实现 相关阅读 Flink使用异步算子 线程池查询MySQL 1 概述 1 Flink异步算子使用介绍 1 异步与同步概述 同步 向数据库发送一个请求然后一
  • 一步步学习SPD2010--第二章节--处理SP网站(6)---- 探索SP网站

    SP技术没有一个界面 你可以通过使用Web浏览器或者兼容程序如Office 应用程序 包括SPD 你可以选择适合你必须完成的任务的接口 然而 根据你选择的程序 你可能有SP网站的不同视图 如果你使用MS Word 你只看到了网站和内容的一小
  • 爆肝整理 JVM 十大模块知识点总结,不信你还不懂

    01 JVM 内存结构 Java 虚拟机的内存空间分为 5 个部分 程序计数器 Java 虚拟机栈 本地方法栈 堆 方法区 JDK 1 8 同 JDK 1 7 比 最大的差别就是 元数据区取代了永久代 元空间的本质和永久 代类似 都是对 J
  • 【ESP32开发】——RGB LED灯(灯珠)点亮

    一 引言 本章内容主要介绍如何使用ESP32开发板点亮板载的RGB灯 使用的是ESP32 S3和ESP32 C3开发板 调用第三方库实现 由于网络上没有关于点亮ESP32板载RGB灯的资料 特此记录 二 ESP32 C3与ESP32 S3
  • python怎么批量处理数据_python操作数据之批量添加数据

    import pymysql import random import time from datetime import datetime type dict 测试01 001 测试02 002 测试03 003 测试04 004 fid
  • bind详解

    bind与占位符 绑定普通函数 绑定成员函数 绑定函数对象 如果你还在使用bind1st bind2nd 那么恐怕已经out了 Boost提供了更强大的武器bind 用于函数对象的绑定 bind接受的第一个参数必须是可调用对象f 包括函数
  • CentOS7.2安装Weblogic12c出现的问题

    Weblogic12c安装到步骤 Prerequisite Checks 时 会进行操作系统版本的校验 即checking operating system certification 此处操作系统版本会校验不过去 如下图 解决方案 修改
  • 刷脸生物支付有唯一性和不可替代性

    刷脸支付的时代 特别是刷脸支付解决了扫码支付需要手机为载体的痛点 在移动支付中生物支付以其唯一性和不可替代性 正在逐步替代传统的金融支付工具 引领未来支付结算模式 移动支付衍生的刷脸支付其核心技术就是人工智能中人脸识别技术 刷脸支付自然也是
  • 0027算法笔记——【回溯法】回溯法与装载问题

    1 回溯法 1 描述 回溯法是一种选优搜索法 按选优条件向前搜索 以达到目标 但当探索到某一步时 发现原先选择并不优或达不到目标 就退回一步重新选择 这种走不通就退回再走的技术为回溯法 2 原理 回溯法在问题的解空间树中 按深度优先策略 从
  • 自定义时间Toast(只弹一次)

    CToast类 package com pinkman dota util import com pinkman dota R import android content Context import android graphics C
  • Linux 系统的运行级别(Run Level)

    Linux 操作系统自从开始启动至启动完毕需要经历几个不同的阶段 这几个阶段就叫做 Runlevel 同样 当Linux操作系统关闭时也要经历另外几个不同的 Runlevel 下面详细介绍一下 Runlevel 并展示一些小技巧来让Linu
  • 2021最新Java常用开源库总结,思维导图+源代码+笔记+项目

    蚂蚁金服 五面 蚂蚁金服 一面 一面就做了一道算法题 要求两小时内完成 给了长度为N的有重复元素的数组 要求输出第10大的数 典型的TopK问题 快排算法搞定 算法题要注意的是合法性校验 边界条件以及异常的处理 另外 如果要写测试用例 一定
  • 2020-10-24 无内容,请勿点击

    1024
  • idea启动Tomcat报错:Application Server was not connected before run configuration stop..

    使用IDEA开启Tomcat时候报了这么个错 Application Server was not connected before run configuration stop reason Unable to ping server a
  • Java3D的场景图结构

    Java3D的场景图结构 Java3D实际上是Java语言在三维图形领域的扩展 与Java一样 Java3D有纯粹的面向对象结构 Java3D的数据结构采用的是Scene Graphs Structure 场景图 就是一些具有方向性的不对称
  • java设计模式——观察者模式(Observer Pattern)

    概述 观察者模式是使用频率最高的设计模式之一 它用于建立一种对象与对象之间的依赖关系 一个对象发生改变时将自动通知其他对象 其他对象将相应作出反应 在观察者模式中 发生改变的对象称为观察目标 而被通知的对象称为观察者 一个观察目标可以对应多
  • [Linux用户空间编程-3]:Linux定时机制的几种实现方法

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 123376014 目录 前言 定时器的