当前位置: 首页 > news >正文

Linux(驱动中) 时间管理和内核定时器(学习总结)

        在Linux内核中,时间管理和内核定时是非常重要的功能,用于实现定时任务、延时操作和周期性事件处理等。内核提供了多种机制来处理时间相关的任务,包括软件定时器(software timers)、硬件定时器(hardware timers)、定时函数(timer functions)等。

一、时间管理     

        Linux内核中的时间管理主要涉及到系统时间的获取、更新和同步,以及提供精确的延时和调度功能。时间管理主要由以下几个部分组成:

  1. 实时时钟(RTC)
    • RTC是硬件设备,用于在系统关机时保持时间信息。
    • 它通过CMOS电池供电,确保在系统重启后能够恢复上一次的系统时间。
    • 内核在启动时读取RTC中的时间信息来初始化系统时间。
  2. 系统定时器
    • 系统定时器是内核中用于产生时钟中断的硬件设备或软件模拟设备。
    • 它以固定的频率(节拍率,Hz)产生中断,内核在中断处理程序中更新系统时间并执行与时间相关的任务。
  3. 全局变量jiffies:
    • jiffies是内核中用于记录自系统启动以来时钟中断次数的全局变量。
    • 它以无符号长整数形式存储,每次时钟中断发生时增加1。
    • jiffies的值可以用于计算系统已经运行的时间(通过jiffies / HZ得到秒数)。
  4. 时间同步
    • 为了确保系统时间的准确性,Linux提供了网络时间协议(NTP)来与其他时间服务器同步时间。
    • NTP客户端会定期向NTP服务器发送请求,并根据服务器的响应调整系统时间。

二、内核定时器

        在 Linux 内核中,定时器机制是一个非常重要的功能,用于在特定时间后执行某些任务或周期性地执行任务。与裸机编程中直接操作硬件定时器不同,Linux 内核提供了一套更为抽象和易用的定时器接口。

软件定时器

        Linux 内核定时器是基于系统时钟实现的,而不是直接基于硬件定时器如 PIT(Programmable Interval Timer)等。内核定时器的使用相对简单,开发者只需提供超时时间和定时处理函数即可。当超时时间到达时,内核会自动调用设置的定时处理函数。

在 Linux 内核中,timer_list 结构体(在较新的内核版本中,通常使用 struct timer_list)用于表示一个内核定时器。这个结构体定义在内核源码的某个头文件中,通常是 <linux/timer.h>

以下是 struct timer_list 结构体的大致定义(具体定义可能因内核版本而异):

struct timer_list {  /* ... 其他成员变量 ... */  struct list_head entry;    /* 定时器链表入口 */  unsigned long expires;     /* 定时器超时时间(以 jiffies 为单位)*/  void (*function)(unsigned long); /* 定时器处理函数 */  unsigned long data;        /* 传递给处理函数的数据 */  /* ... 可能还有其他成员变量,如定时器状态、回调参数等 ... */  
};
主要API函数:
  • TIMER_INITIALIZER(name, function, expires, data):宏定义,用于静态初始化定时器。
  • INIT_TIMER(&timer):函数,用于动态初始化定时器。
  • del_timer_sync(&timer):删除并同步定时器,确保定时器不再被执行。
  • del_timer(&timer):删除定时器,但不等待定时器完成。
  • add_timer(&timer):添加定时器,使其在指定时间后启动。
  • mod_timer(&timer, expires):修改定时器的过期时间。
  • timer_setup(&timer, function, expires):设置定时器回调函数和过期时间。
  1. 使用内核定时器的步骤
    • 定义timer_list结构体变量。
    • 使用init_timer函数初始化定时器。
    • 设置定时器的到期时间(通过expires成员)和处理函数(通过function成员)。
    • 使用add_timer函数将定时器添加到内核中并启动它。
    • 如果需要,可以使用mod_timer函数修改定时器的到期时间。
    • 当不再需要定时器时,使用del_timer函数从内核中删除它。

示例:

#include <linux/timer.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>static struct timer_list my_timer;static unsigned int interval = 1;  // 定时器间隔,单位为秒static void timer_callback(unsigned long data)
{printk(KERN_INFO "Timer expired!\n");// 在这里执行定时任务mod_timer(&my_timer, jiffies + interval * HZ);
}static int __init timer_example_init(void)
{printk(KERN_INFO "Timer example module loaded.\n");// 初始化定时器INIT_TIMER(&my_timer);my_timer.function = timer_callback;my_timer.expires = jiffies + interval * HZ;my_timer.data = 0;// 添加定时器add_timer(&my_timer);return 0;
}static void __exit timer_example_exit(void)
{del_timer_sync(&my_timer);  // 删除并同步定时器printk(KERN_INFO "Timer example module unloaded.\n");
}module_init(timer_example_init);
module_exit(timer_example_exit);
MODULE_LICENSE("GPL");
 硬件定时器:

        硬件定时器通常由硬件提供支持,例如ARM平台上的RTC(实时时钟)。硬件定时器可以提供更精确的定时服务,并且通常独立于操作系统的工作。

示例:

#include <linux/hrtimer.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>struct hrtimer my_hrtimer;static unsigned int interval_ns = 1000000000;  // 定时器间隔,单位为纳秒static enum hrtimer_restart hrtimer_callback(struct hrtimer *hrt)
{printk(KERN_INFO "High-resolution timer expired!\n");// 在这里执行定时任务hrtimer_forward(&my_hrtimer, hrt_get_base_now(), ns_to_ktime(interval_ns));return HRTIMER_RESTART;
}static int __init hrtimer_example_init(void)
{printk(KERN_INFO "High-resolution timer example module loaded.\n");// 初始化高分辨率定时器INIT_HRTIMER(&my_hrtimer);my_hrtimer.function = hrtimer_callback;// 启动定时器hrtimer_start(&my_hrtimer, ns_to_ktime(interval_ns), HRTIMER_MODE_RELATIVE);return 0;
}static void __exit hrtimer_example_exit(void)
{hrtimer_cancel(&my_hrtimer);  // 取消定时器printk(KERN_INFO "High-resolution timer example module unloaded.\n");
}module_init(hrtimer_example_init);
module_exit(hrtimer_example_exit);
MODULE_LICENSE("GPL");

        除了上述的软件定时器和硬件定时器外,Linux内核还提供了一些辅助函数来处理定时任务,如delay()系列函数和schedule_timeout()函数。

延迟函数:
  • msleep(msec):休眠指定的毫秒数。
  • usleep(usec):休眠指定的微秒数。
  • mdelay(msec):延迟指定的毫秒数。
  • udelay(usec):延迟指定的微秒数。

示例:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>static void delay_example(void)
{printk(KERN_INFO "Before delay.\n");msleep(1000);  // 延迟1秒printk(KERN_INFO "After delay.\n");
}static int __init delay_example_init(void)
{printk(KERN_INFO "Delay example module loaded.\n");delay_example();return 0;
}static void __exit delay_example_exit(void)
{printk(KERN_INFO "Delay example module unloaded.\n");
}module_init(delay_example_init);
module_exit(delay_example_exit);
MODULE_LICENSE("GPL");
定时等待函数:
  • schedule_timeout(timeout):等待指定的时间后调度其他任务。
  • schedule_timeout_uninterruptible(timeout):不可中断地等待指定的时间后调度其他任务。

示例:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>static void timeout_example(void)
{printk(KERN_INFO "Before timeout.\n");schedule_timeout(1 * HZ);  // 延迟1秒printk(KERN_INFO "After timeout.\n");
}static int __init timeout_example_init(void)
{printk(KERN_INFO "Timeout example module loaded.\n");timeout_example();return 0;
}static void __exit timeout_example_exit(void)
{printk(KERN_INFO "Timeout example module unloaded.\n");
}module_init(timeout_example_init);
module_exit(timeout_example_exit);
MODULE_LICENSE("GPL");

小结 

        在Linux系统及其驱动开发中,软件定时器和硬件定时器各有优缺点,适用于不同的场景。软件定时器具有跨平台性好、灵活性高的特点,但精度受限;而硬件定时器则具有高精度和稳定性好的特点,但跨平台性差。在实际应用中,开发者应根据具体需求选择合适的定时器实现方式。

ps:在学习工程中的简单记录,作为学习记录,不够严谨,欢迎指正

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Ollama—87.4k star 的开源大模型服务框架!!
  • Linux驱动.之驱动开发思维,设备,驱动,总线分析思想,驱动的分类(字符设备,块设备,网络设备)
  • 降低安全违规行为发生率,节省人工监管成本的智慧园区开源了
  • iOS面试:如何手动触发一个value的KVO?
  • Qt-桌面服务和托盘
  • GPU环境配置:1.CUDA、Anaconda、Pytorch
  • 备份还原 本地所有的Docker 镜像并且在另一台机器上还原
  • bios中启动模式uefi是什么意思_uefi相关知识史上最全介绍
  • 超声波测距模块HC-SR04(基于STM32F103C8T6HAL库)
  • [米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-39 HDMI视频输入测试
  • 我司使用了两年的高效日志打印工具,非常牛逼!
  • 【C++】优化函数对象:提升性能和内存效率
  • 第十六篇:走入计算机网络的传输层--传输层概述
  • 【Linux 运维知识】Linux 编译后的内核镜像大小
  • elementplus表单位置居中
  • [LeetCode] Wiggle Sort
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 230. Kth Smallest Element in a BST
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • express.js的介绍及使用
  • Lsb图片隐写
  • python学习笔记 - ThreadLocal
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 爬虫模拟登陆 SegmentFault
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 什么是Javascript函数节流?
  • 使用docker-compose进行多节点部署
  • 数据结构java版之冒泡排序及优化
  • 算法---两个栈实现一个队列
  • 小李飞刀:SQL题目刷起来!
  • 原生Ajax
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #C++ 智能指针 std::unique_ptr 、std::shared_ptr 和 std::weak_ptr
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • $forceUpdate()函数
  • (1)SpringCloud 整合Python
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (附源码)计算机毕业设计大学生兼职系统
  • (函数)颠倒字符串顺序(C语言)
  • (每日一问)操作系统:常见的 Linux 指令详解
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (全注解开发)学习Spring-MVC的第三天
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (杂交版)植物大战僵尸
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)可以带来幸福的一本书
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • ..thread“main“ com.fasterxml.jackson.databind.JsonMappingException: Jackson version is too old 2.3.1
  • .NET Core中的去虚
  • .NET NPOI导出Excel详解