Linux驱动开发—Linux内核定时器概念和使用详解,实现基于定时器的字符驱动

文章目录

    • 内核定时器概念
    • 在Linux驱动模块中使用定时器
      • 软定时器(Soft Timers)
      • jiffies 含义
      • 高精度定时器(High Resolution Timers)
    • 实现倒计时字符设备驱动

内核定时器概念

在 Linux 内核中,定时器是用来管理和调度延迟操作的机制。Linux 内核提供了几种不同类型的定时器,每种定时器都有不同的特点和用途。以下是一些主要的定时器类型和相关概念:

  1. POSIX 定时器

    • 这是一个基于 POSIX 标准的定时器,提供了一种用户空间的定时器接口。通过 timer_create()timer_settime()timer_gettime() 等系统调用,用户空间程序可以创建和管理定时器。
  2. 高精度定时器 (High Resolution Timers)

    • 高精度定时器可以提供比普通定时器更高的时间精度。它们使用 hrtimer API 来实现,适用于需要非常精确定时的场景,比如实时系统。
  3. 软定时器 (Soft Timers)

    • 软定时器是内核中的定时器,用于调度执行内核中的延迟操作。它们主要通过 mod_timer()del_timer() 等 API 进行管理。软定时器通常用于处理网络数据包或其他延迟任务。
  4. 延迟队列 (Delay Queues)

    • 延迟队列是一种数据结构,用于在内核中排队等待延迟处理的任务。通过 init_timer()add_timer() 等函数,可以将任务添加到延迟队列中,等待指定时间后执行。
  5. 定时器回调

    • 当定时器到期时,会调用相应的回调函数来执行预定的操作。回调函数通常会在内核态执行,进行一些需要延迟处理的任务。

定时器在内核中的实现涉及到多种机制,包括定时器队列、定时器中断以及内核调度机制等。具体使用和实现细节可以参考 Linux 内核文档和源代码。

在Linux驱动模块中使用定时器

在 Linux 驱动模块中使用定时器,通常有两种主要方式:使用软定时器和高精度定时器。

软定时器(Soft Timers)

软定时器在内核驱动中使用时,通常涉及到 timer_list 结构和相关 API。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/init.h>static struct timer_list my_timer;void timer_callback(struct timer_list *t)
{printk(KERN_INFO "Timer callback function called.\n");
}static int __init my_module_init(void)
{printk(KERN_INFO "Module initialized.\n");printk(KERN_INFO "System HZ value: %d\n", HZ);// 初始化定时器timer_setup(&my_timer, timer_callback, 0);// 设置定时器超时值(例如 500ms)mod_timer(&my_timer, jiffies + msecs_to_jiffies(500));return 0;
}static void __exit my_module_exit(void)
{// 删除定时器del_timer(&my_timer);printk(KERN_INFO "Module exited.\n");
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marxist");
MODULE_DESCRIPTION("A simple Linux driver with a timer.");

关键点说明

  • timer_setup 函数用于初始化 timer_list 结构体并设置回调函数。
  • mod_timer 函数用于设置定时器的超时时间。
  • del_timer 函数用于删除定时器,防止模块卸载时定时器仍在执行。

效果如图:

其中 printk(KERN_INFO "System HZ value: %d\n", HZ); 输出当前系统的时钟中断频率,与jiffies 相关

在这里插入图片描述

jiffies 含义

在 Linux 内核中,jiffies 是一个用于表示系统启动以来的时间的全局计数器,它以“滴答”(tick)为单位。jiffies 是内核中时间管理的一个基本概念,广泛用于定时器和延迟操作。

定义:jiffies 是一个计数器,表示自系统启动以来经过的“时钟滴答”数。每个滴答的时间长度由系统的时钟中断频率决定,通常是 1 毫秒(即 1000 Hz)或 10 毫秒(即 100 Hz),但具体取决于内核配置。

单位jiffies 的单位是系统时钟滴答。系统时钟滴答的频率(即每秒钟中断的次数)由内核配置中的 HZ 参数决定。例如,如果 HZ 设置为 1000,那么每秒会有 1000 个滴答,每个滴答大约是 1 毫秒。

计算时间:使用 jiffies 可以计算时间间隔。例如,如果你要设置一个定时器在 500 毫秒后触发,可以使用以下代码:

mod_timer(&my_timer, jiffies + msecs_to_jiffies(500));

这里,msecs_to_jiffies 函数将 500 毫秒转换为相应的 jiffies 数量。

溢出:由于 jiffies 是一个 unsigned long 类型的变量,它可能会在长时间运行后溢出。在现代 Linux 内核中,jiffies 的溢出问题一般不会对大多数应用造成问题,因为内核设计考虑了这个问题并进行了处理。

转换函数

  • jiffies_to_msecs(jiffies):将 jiffies 转换为毫秒。
  • jiffies_to_timespec(jiffies):将 jiffies 转换为 timespec 结构。
  • msecs_to_jiffies(milliseconds):将毫秒转换为 jiffies

高精度定时器(High Resolution Timers)

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/init.h>static struct hrtimer my_hrtimer;
static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
{printk(KERN_INFO "High resolution timer callback function called.\n");return HRTIMER_NORESTART; // 不重复定时
}static int __init my_module_init(void)
{printk(KERN_INFO "Module initialized.\n");// 初始化高精度定时器hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);my_hrtimer.function = hrtimer_callback;// 设置定时器超时值(例如 500ms)ktime_t ktime = ktime_set(0, 500 * 1000000); // 500mshrtimer_start(&my_hrtimer, ktime, HRTIMER_MODE_REL);return 0;
}static void __exit my_module_exit(void)
{// 删除高精度定时器int ret = hrtimer_cancel(&my_hrtimer);if (ret)printk(KERN_INFO "The high resolution timer was still active.\n");printk(KERN_INFO "Module exited.\n");
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marxist");
MODULE_DESCRIPTION("A simple Linux driver with a high resolution timer.");

关键点说明

  • hrtimer_init 函数用于初始化高精度定时器。
  • hrtimer_start 函数用于设置高精度定时器的超时时间。
  • ktime_set 用于创建高精度时间值。
  • hrtimer_cancel 函数用于取消高精度定时器。

效果如下:

在这里插入图片描述

实现倒计时字符设备驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/atomic.h> 
#define DEVICE_NAME "countdown_device"static atomic_t counter = ATOMIC_INIT(0);static int major_number;
static struct class *my_class = NULL;
static struct device *my_device = NULL;
static struct cdev mydev;             // 声明 cdev 结构体
static struct timer_list my_timer;    // 声明定时器
static struct semaphore my_semaphore; // 定义信号量
static int count_val = 30;            // 倒计时初始值// 定时器回调函数
void timer_callback(struct timer_list *t)
{count_val= atomic_read(&counter);// 每秒 -1if (count_val > 0){atomic_dec(&counter);printk("mytimer : %d",atomic_read(&counter));// 重新设置定时器以在 1 秒后触发mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));}else count_val = -1; // 代表结束
}static int device_open(struct inode *inode, struct file *file)
{printk(KERN_INFO "Device opened\n");// 初始化定时器 ,并开启定时任务timer_setup(&my_timer, timer_callback, 0);mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));return 0;
}static int device_release(struct inode *inode, struct file *file)
{printk(KERN_INFO "Device closed\n");// 删除定时器del_timer_sync(&my_timer);return 0;
}static ssize_t device_read(struct file *filp, char __user *buffer, size_t len, loff_t *offset)
{int count_val_copy;count_val_copy = atomic_read(&counter);printk("begin read new val: %d", count_val_copy);// 将数据从内核空间拷贝到用户空间if (copy_to_user(buffer, &count_val_copy, sizeof(count_val_copy))){printk("copy_to_user error");return -EFAULT;}return sizeof(count_val_copy);
}static ssize_t device_write(struct file *filp, const char __user *buffer, size_t len, loff_t *offset)
{char buf[64];int new_value;// 获取信号量if (down_interruptible(&my_semaphore)){return -ERESTARTSYS;}// 从用户空间复制数据if (copy_from_user(buf, buffer, len)){up(&my_semaphore);return -EFAULT;}buf[len] = '\0';if (sscanf(buf, "%d", &new_value) == 1){count_val = new_value; // 从用户空间获取到倒计时的值}printk("write new val is %d",count_val);// 释放信号量up(&my_semaphore);//重新给原子变量赋值atomic_set(&counter, count_val);  return len;
}static struct file_operations fops = {.open = device_open,.release = device_release,.read = device_read,.write = device_write,
};static int __init test_init(void)
{int retval;dev_t dev;printk(KERN_INFO "module init success\n");// 初始化信号量sema_init(&my_semaphore, 1);// 1. 动态分配主次设备号retval = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);if (retval < 0){printk(KERN_ERR "Failed to allocate major number\n");goto fail_alloc_chrdev_region;}major_number = MAJOR(dev);printk(KERN_INFO "major number is: %d, minor number is: %d\n", major_number, MINOR(dev));// 2. 初始化 cdev 结构体并添加到内核cdev_init(&mydev, &fops);retval = cdev_add(&mydev, dev, 1);if (retval < 0){printk(KERN_ERR "Failed to add cdev\n");goto fail_cdev_add;}// 3. 创建设备类my_class = class_create(THIS_MODULE, "my_class");if (IS_ERR(my_class)){printk(KERN_ERR "Failed to create class\n");retval = PTR_ERR(my_class);goto fail_class_create;}// 4. 申请设备,内核空间就会通知用户空间的 udev 进行创建设备,驱动程序本身自己是创建不了文件的!my_device = device_create(my_class, NULL, dev, NULL, DEVICE_NAME);if (IS_ERR(my_device)){printk(KERN_ERR "Failed to create device\n");retval = PTR_ERR(my_device);goto fail_device_create;}printk(KERN_INFO "my_char_device: module loaded\n");return 0;fail_device_create:class_destroy(my_class);
fail_class_create:cdev_del(&mydev);
fail_cdev_add:unregister_chrdev_region(dev, 1);
fail_alloc_chrdev_region:return retval;
}static void __exit test_exit(void)
{dev_t dev = MKDEV(major_number, 0);if (my_device)device_destroy(my_class, dev);if (my_class)class_destroy(my_class);cdev_del(&mydev);unregister_chrdev_region(dev, 1);printk(KERN_INFO "my_char_device: module unloaded\n");
}module_init(test_init);
module_exit(test_exit);
MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");

用户端

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>#define DEVICE_PATH "/dev/countdown_device"int main()
{int fd;char buffer[64];int count_val = 60;  // 倒计时初始值ssize_t bytes_written, bytes_read;int res_val;// 打开设备文件fd = open(DEVICE_PATH, O_RDWR);if (fd < 0){perror("Failed to open the device");return errno;}// 写入倒计时初始值snprintf(buffer, sizeof(buffer), "%d", count_val);bytes_written = write(fd, buffer, strlen(buffer));if (bytes_written < 0){perror("Failed to write to the device");close(fd);return errno;}printf("Written %d to the device\n", count_val);// 进行倒计时读取输出while (1){// 读取当前倒计时值bytes_read = read(fd, &res_val, sizeof(res_val));if (bytes_read < 0){perror("Failed to read from the device");close(fd);return errno;}// 打印当前倒计时值printf("Current countdown value: %d\n", res_val);// 休眠1秒sleep(1);// 判断倒计时是否结束if (res_val <= 0){printf("Countdown finished.\n");break;}}// 关闭设备文件close(fd);return 0;
}

效果如图:在内核中,使用定时器每秒对原子变量的值减-1

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/394445.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8.7-主从数据库的配置+mysql的增删改查

一、mysql环境的配置 1.环境准备 &#xff08;1&#xff09;主数据库 #关闭防火墙 [rootmaster ~]# systemctl stop firewalld#关闭selinux [rootmaster ~]# setenforce 0#下载lrzsz工具 [rootmaster ~]# yum -y install lrzsz#安装rsync [rootmaster ~]# yum -y install rs…

低代码平台:效率利器还是质量妥协?

目录 低代码平台&#xff1a;效率利器还是质量妥协&#xff1f; 一、引言 二、低代码平台的定义和背景 1、什么是低代码平台&#xff1f; 2、低代码平台的兴起 三、低代码开发的机遇 1、提高开发效率 2、降低开发成本 3、赋能业务人员 四、低代码开发的挑战 1、质量…

pgbackrest备份方案(差异和增量备份的区别)

pgbackrest备份方案(差异和增量备份的区别) 一 备份 全量备份&#xff1a; 将数据库集群的全部内容复制到备份中。数据库集群的第一个备份始终是全量备份。始终能够直接还原全量备份。全量备份不依赖于完整备份之外的任何文件来保持一致性。 差异备份&#xff1a; 仅复制自…

3D展示的前景如何?

随着人类科技的不断进步&#xff0c;对未来的趋势也肯定是向高纬度发展。3D取代2D只是一个所需时间长短而已&#xff0c;题主既然这么问&#xff0c;说明肯定是意识到了3D是未来的趋势&#xff0c;那么就应该多接触和了解未来的3D平台及应用工具、应用领域等。 之前2G\3G时代&…

1.MongoDB入门指南之开篇

1. 写在前面 MongoDB大家可能听说过&#xff0c;但是要怎么学习&#xff1f;先学习哪个&#xff0c;很多人是不知道的&#xff0c;毕竟面对一个未知的事物&#xff0c;迷茫是很多人都会遇到的&#xff0c;从今天起我们就开始系统的介绍MongoDB的学习。 2. 课程介绍 课程主要分…

【Android Studio】原生应用部署第三方插件(探针)

一、本地引入包流程 &#xff08;一&#xff09;本地引入包内容 &#xff08;二&#xff09;本地引入包操作步骤 将 【probe-android-sdk】目录里面所有的aar包复制到嵌码项目工程&#xff08;App级别&#xff09;的 libs 目录下 二、添加插件 &#xff08;一&#xff09;…

Qt文件读写

Qt中文件读写类简述 包含头文件#include <QFile> 读写模式如下 枚举 文件读写步骤 1 先使用string 类型来接受打开文件的返回值 QFileDialog::getOpenFileName(this,"文件","./"); //打开一个文件 2 构建文件对象 Qfile ff (qstring)接受打…

“前缀和”专题篇一

目录 【模版】前缀和 【模版】二维前缀和 寻找数组的中心下标 除自身以外数组的乘积 【模版】前缀和 题目 思路 这道题如果使用暴力解法&#xff0c;即针对每次查询&#xff0c;先算出前r个数的总和&#xff0c;然后再算出前l-1个数的总和&#xff0c;然后相减就得出本次查…

【MYSQL】MYSQL逻辑架构

mysql逻辑架构分为3层 mysql逻辑架构分为3层 1). 连接层&#xff1a;主要完成一些类似连接处理&#xff0c;授权认证及相关的安全方案。 2). 服务层&#xff1a;在 MySQL据库系统处理底层数据之前的所有工作都是在这一层完成的&#xff0c;包括权限判断&#xff0c;SQL接口&…

三数之和-Leetcode

leetcode链接&#xff1a;三数之和 题目描述 解题思路 主要要思考以下几个问题&#xff1a; 如何选取三个元素&#xff1f;— 当前节点 左指针 右指针指针开始位置&#xff1f;— 左指针 当前节点位置 i 1&#xff0c; 右指针 n - 1如何保证不重复&#xff1f; — 先把…

利用自然语言处理(NLP)技术挖掘旅游评论数据

目录 简单了解 延伸 如何使用自然语言处理技术提高旅游评论情感倾向的准确性&#xff1f; 旅游评论数据中多模态信息融合的最佳实践是什么&#xff1f; 在旅游评论数据预处理和清洗过程中&#xff0c;哪些方法最有效&#xff1f; 使用Python网络爬虫技术进行旅游评论数据的…

Python酷库之旅-第三方库Pandas(072)

目录 一、用法精讲 291、pandas.Series.dt.round函数 291-1、语法 291-2、参数 291-3、功能 291-4、返回值 291-5、说明 291-6、用法 291-6-1、数据准备 291-6-2、代码示例 291-6-3、结果输出 292、pandas.Series.dt.floor函数 292-1、语法 292-2、参数 292-3、…

关于手机中的红外遥控

在手机电路中&#xff0c;有这么不起眼的一部分&#xff0c;虽看似简单&#xff0c;但是却给我们的生活在一定程度上带来了极大的便捷-红外遥控部分。 其置于手机顶部&#xff0c;并在壳体处挖开一个小孔&#xff0c;用于红外信号对外界的传递。如果你感兴趣的话&#xff0c;不…

Go语言项目实战班04 Go语言课程管理系统项目实战 20240807 课程笔记和上课代码

预览 课程特色 本教程录制于2024年8月8日&#xff0c;使用Go1.22版本&#xff0c;基于Goland2024进行开发&#xff0c;采用的技术栈比较新。 每节课控制在十分钟以内&#xff0c;课时精简&#xff0c;每节课都是一个独立的知识点&#xff0c;如果有遗忘&#xff0c;完全可以当…

基于JSP技术的人事管理系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a; Java 数据库&#xff1a; MySQL 技术&#xff1a; JSP技术 Java语言 工具&#xff1a; Myeclipse 系统展示 首页 管理员功能模块 员工功能模…

攻击者劫持 Facebook 页面用于推广恶意 AI 照片编辑器

近日&#xff0c;有攻击者劫持了 Facebook 上的网页&#xff0c;诱骗用户下载一个合法的人工智能&#xff08;AI&#xff09;照片编辑器&#xff0c;但实际上他们真正下载的却是一个专门用以盗取用户的凭据信息窃取程序。 趋势科技的研究人员发现的这一恶意广告活动利用了人工…

什么是实时数据仓库?它有哪些不可替代之处?

【实时数据仓库】可以分开来理解&#xff1a; ✅【实时数据】&#xff1a;即能够快速处理数据&#xff0c;且几乎无延迟的提供最新的数据的能力。 ✅【仓库管理】&#xff1a;可以理解为对仓库的库存控制、对仓库的存储优化以及协调物流。 那么实时数据仓库就是&#xff1a;…

Stable Diffusion绘画 | 图生图-批量处理

批量处理中&#xff0c;对待处理图片的要求&#xff1a;宽高比一致 修改提示词后批量处理 调整参数&#xff1a; 确保宽高与原图一致增加一定的重绘幅度 调整提示词信息&#xff1a; 批量处理后&#xff0c;出图如下所示&#xff1a; 修改模型后批量处理 恢复提示词&#xf…

HTTP:从基础概念到协议机制,详解请求响应与状态保持

文章目录 一、HTTP概述1、HTTP的理解2、HTTP是无状态的协议 二、HTTP协议的过程1、URL&#xff08;统一资源定位符&#xff09;2、客户端3、服务器端 三、HTTP请求与响应1、HTTP请求和响应2、HTTP请求方法3、状态码 四、HTTP报文1、请求报文首部2、响应报文首部3、首部字段 五、…

Gstreamer实现udp帧数据的转发(一)

前言 最近有个项目&#xff0c;要求实现信息分发&#xff0c;大概意思是经过了各种交换机和电台&#xff0c;经过两个信息分发软件实现udp数据的转发&#xff0c;可能包括文本、指令、音视频等数据。 例如&#xff1a;设备1 《---》 设备2&#xff08;信息分发软件1&#xff09…