【Linux-时间管理和内核定时器】

Linux-时间管理和内核定时器

  • ■ 设置系统节拍率
  • ■ 高节拍率和低节拍率的优缺点:
  • ■ jiffies 系统节拍数
    • ■ get_jiffies_64 这个函数可以获取 jiffies_64 的值
    • ■ 处理绕回
    • ■ 使用 jiffies 判断超时
  • ■ jiffies 和 ms、 us、 ns 之间的转换函数`在这里插入代码片`
  • ■ 内核定时器
    • ■ 内核定时器API
    • ■ 内核短延时函数
    • ■ 示例一:使用范例
    • ■ 示例二:Linux内核定时器实验 内核定时器周期性的点亮和熄灭开发板上的 LED灯

■ 设置系统节拍率

在这里插入图片描述
在这里插入图片描述
默认情况下选择 100Hz。设置好以后打开 Linux 内核源码根目录下的.config 文件,
在这里插入图片描述

打开文件 include/asm-generic/param.h,
在这里插入图片描述

■ 高节拍率和低节拍率的优缺点:

①、高节拍率会提高系统时间精度,
如果采用 100Hz 的节拍率,时间精度就是 10ms,
采用1000Hz 的话时间精度就是 1ms,精度提高了 10 倍。
高精度时钟的好处有很多,对于那些对时间要求严格的函数来说,能够以更高的精度运行,时间测量也更加准确。

②、高节拍率会导致中断的产生更加频繁,频繁的中断会加剧系统的负担,
1000Hz 和 100Hz的系统节拍率相比,系统要花费 10 倍的“精力”去处理中断。
中断服务函数占用处理器的时间增加,但是现在的处理器性能都很强大,所以采用 1000Hz 的系统节拍率并不会增加太大的负载压力。
根据自己的实际情况,选择合适的系统节拍率,本教程我们全部采用默认的 100Hz 系统节拍率。

■ jiffies 系统节拍数

Linux 内核使用全局变量 jiffies 来记录系统从启动以来的系统节拍数,系统启动的时候会将 jiffies 初始化为 0, jiffies 定义在文件 include/linux/jiffies.h 中

extern u64 __jiffy_data jiffies_64;     //定义了一个 64 位的 jiffies_64。
extern unsigned long volatile __jiffy_data jiffies;    //定义了一个 unsigned long 类型的 32 位的 jiffies。

jiffies_64用于 64 位系统,而 jiffies 用于 32 位系统.
当我们访问 jiffies 的时候其实访问的是 jiffies_64 的低 32 位,

■ get_jiffies_64 这个函数可以获取 jiffies_64 的值

HZ 表示每秒的节拍数, jiffies 表示系统运行的 jiffies 节拍数,所以 jiffies/HZ 就是系统运行时间,单位为秒。

■ 处理绕回

假如 HZ 为最大值 1000 的时候,
对于 32 位的 jiffies 只需要 49.7 天就发生了绕回,
对于 64 位的 jiffies 来说大概需要5.8 亿年才能绕回,因此 jiffies_64 的绕回忽略不计。处理 32 位 jiffies 的绕回显得尤为重要

函数描述
time_after(unkown, known)unkown 超过 known 的话, time_after 函数返回真,否则返回假
time_before(unkown, known)unkown 没有超过 known 的话 time_before 函数返回真,否则返回假
time_after_eq(unkown, known)和 time_after 函数类似 只是多了判断等于这个条件
time_before_eq(unkown, known)和 time_before函数类似 只是多了判断等于这个条件

■ 使用 jiffies 判断超时

timeout 就是超时时间点,比如我们要判断代码执行时间是不是超过了 2 秒
在这里插入图片描述

■ jiffies 和 ms、 us、 ns 之间的转换函数在这里插入代码片

函数描述
int jiffies_to_msecs(const unsigned long j)将 jiffies 类型的参数 j 分别转换为对应的毫秒
int jiffies_to_usecs(const unsigned long j)将 jiffies 类型的参数 j 分别转换为对应的微秒
u64 jiffies_to_nsecs(const unsigned long j)将 jiffies 类型的参数 j 分别转换为对应的纳秒
long msecs_to_jiffies(const unsigned int m)将毫秒转换为 jiffies 类型。
long usecs_to_jiffies(const unsigned int u)将微秒转换为 jiffies 类型。
unsigned long nsecs_to_jiffies(u64 n)将纳秒转换为 jiffies 类型。

■ 内核定时器

Linux 内核定时器使用很简单,只需要提供超时时间(相当于定时值)和定时处理函数即可。
Linux 内核使用 timer_list 结构体表示内核定时器, timer_list 定义在文件include/linux/timer.h 中

struct timer_list {
	struct list_head entry;
	unsigned long expires; /* 定时器超时时间,单位是节拍数 */
	struct tvec_base *base;
	void (*function)(unsigned long); /* 定时处理函数 */
	unsigned long data; /* 要传递给 function 函数的参数 */
	int slack;
};

■ 内核定时器API

函数描述
init_timer 函数初始化 timer_list 类型变量,
add_timer 函数向 Linux 内核注册定时器,
del_timer 函数删除一个定时器
del_timer_sync 函数del_timer_sync 函数是 del_timer 函数的同步版,会等待其他处理器使用完定时器再删除,del_timer_sync 不能使用在中断上下文中。
mod_timer 函数修改定时值

■ 内核短延时函数

Linux 内核提供了毫秒、微秒 、纳秒 延时函数
在这里插入图片描述

■ 示例一:使用范例

struct timer_list timer; /* 定义定时器 */

/* 定时器回调函数 */
void function(unsigned long arg)
{
	/*
	* 定时器处理代码
	*/
	
	/* 如果需要定时器周期性运行的话就使用 mod_timer
	* 函数重新设置超时值并且启动定时器。
	*/
	mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));
}

/* 初始化函数 */
void init(void)
{
	init_timer(&timer); /* 初始化定时器 */	
	timer.function = function; /* 设置定时处理函数 */
	timer.expires=jffies + msecs_to_jiffies(2000);/* 超时时间 2 秒 */
	timer.data = (unsigned long)&dev; /* 将设备结构体作为参数 */	
	add_timer(&timer); /* 启动定时器 */
}

/* 退出函数 */
void exit(void)
{
	del_timer(&timer); /* 删除定时器 */
	/* 或者使用 */
	del_timer_sync(&timer);
}

■ 示例二:Linux内核定时器实验 内核定时器周期性的点亮和熄灭开发板上的 LED灯

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define TIMER_CNT		1		/* 设备号个数 	*/
#define TIMER_NAME		"timer"	/* 名字 		*/
#define CLOSE_CMD 		(_IO(0XEF, 0x1))	/* 关闭定时器 */
#define OPEN_CMD		(_IO(0XEF, 0x2))	/* 打开定时器 */
#define SETPERIOD_CMD	(_IO(0XEF, 0x3))	/* 设置定时器周期命令 */
#define LEDON 			1		/* 开灯 */
#define LEDOFF 			0		/* 关灯 */

/* timer设备结构体 */
struct timer_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
	struct device_node	*nd; /* 设备节点 */
	int led_gpio;			/* key所使用的GPIO编号		*/
	int timeperiod; 		/* 定时周期,单位为ms */
	struct timer_list timer;/* 定义一个定时器*/
	spinlock_t lock;		/* 定义自旋锁 */
};

struct timer_dev timerdev;	/* timer设备 */

/*
 * @description	: 初始化LED灯IO,open函数打开驱动的时候
 * 				  初始化LED灯所使用的GPIO引脚。
 * @param 		: 无
 * @return 		: 无
 */
static int led_init(void)
{
	int ret = 0;

	timerdev.nd = of_find_node_by_path("/gpioled");
	if (timerdev.nd== NULL) {
		return -EINVAL;
	}

	timerdev.led_gpio = of_get_named_gpio(timerdev.nd ,"led-gpio", 0);
	if (timerdev.led_gpio < 0) {
		printk("can't get led\r\n");
		return -EINVAL;
	}
	
	/* 初始化led所使用的IO */
	gpio_request(timerdev.led_gpio, "led");		/* 请求IO 	*/
	ret = gpio_direction_output(timerdev.led_gpio, 1);
	if(ret < 0) {
		printk("can't set gpio!\r\n");
	}
	return 0;
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int timer_open(struct inode *inode, struct file *filp)
{
	int ret = 0;
	filp->private_data = &timerdev;	/* 设置私有数据 */

	timerdev.timeperiod = 1000;		/* 默认周期为1s */
	ret = led_init();				/* 初始化LED IO */
	if (ret < 0) {
		return ret;
	}
	return 0;
}

/*
 * @description		: ioctl函数,
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - cmd 	: 应用程序发送过来的命令
 * @param - arg 	: 参数
 * @return 			: 0 成功;其他 失败
 */
static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct timer_dev *dev =  (struct timer_dev *)filp->private_data;
	int timerperiod;
	unsigned long flags;
	
	switch (cmd) {
		case CLOSE_CMD:		/* 关闭定时器 */
			del_timer_sync(&dev->timer);
			break;
		case OPEN_CMD:		/* 打开定时器 */
			spin_lock_irqsave(&dev->lock, flags);
			timerperiod = dev->timeperiod;
			spin_unlock_irqrestore(&dev->lock, flags);
			mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));
			break;
		case SETPERIOD_CMD: /* 设置定时器周期 */
			spin_lock_irqsave(&dev->lock, flags);
			dev->timeperiod = arg;
			spin_unlock_irqrestore(&dev->lock, flags);
			mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
			break;
		default:
			break;
	}
	return 0;
}

/* 设备操作函数 */
static struct file_operations timer_fops = {
	.owner = THIS_MODULE,
	.open = timer_open,
	.unlocked_ioctl = timer_unlocked_ioctl,
};

/* 定时器回调函数 */
void timer_function(unsigned long arg)
{
	struct timer_dev *dev = (struct timer_dev *)arg;
	static int sta = 1;
	int timerperiod;
	unsigned long flags;

	sta = !sta;		/* 每次都取反,实现LED灯反转 */
	gpio_set_value(dev->led_gpio, sta);
	
	/* 重启定时器 */
	spin_lock_irqsave(&dev->lock, flags);
	timerperiod = dev->timeperiod;
	spin_unlock_irqrestore(&dev->lock, flags);
	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod)); 
 }

/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init timer_init(void)
{
	/* 初始化自旋锁 */
	spin_lock_init(&timerdev.lock);

	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if (timerdev.major) {		/*  定义了设备号 */
		timerdev.devid = MKDEV(timerdev.major, 0);
		register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME);
	} else {						/* 没有定义设备号 */
		alloc_chrdev_region(&timerdev.devid, 0, TIMER_CNT, TIMER_NAME);	/* 申请设备号 */
		timerdev.major = MAJOR(timerdev.devid);	/* 获取分配号的主设备号 */
		timerdev.minor = MINOR(timerdev.devid);	/* 获取分配号的次设备号 */
	}
	
	/* 2、初始化cdev */
	timerdev.cdev.owner = THIS_MODULE;
	cdev_init(&timerdev.cdev, &timer_fops);
	
	/* 3、添加一个cdev */
	cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);

	/* 4、创建类 */
	timerdev.class = class_create(THIS_MODULE, TIMER_NAME);
	if (IS_ERR(timerdev.class)) {
		return PTR_ERR(timerdev.class);
	}

	/* 5、创建设备 */
	timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME);
	if (IS_ERR(timerdev.device)) {
		return PTR_ERR(timerdev.device);
	}
	
	/* 6、初始化timer,设置定时器处理函数,还未设置周期,所有不会激活定时器 */
	init_timer(&timerdev.timer);
	timerdev.timer.function = timer_function;
	timerdev.timer.data = (unsigned long)&timerdev;
	return 0;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit timer_exit(void)
{
	
	gpio_set_value(timerdev.led_gpio, 1);	/* 卸载驱动的时候关闭LED */
	del_timer_sync(&timerdev.timer);		/* 删除timer */
#if 0
	del_timer(&timerdev.tiemr);
#endif

	/* 注销字符设备驱动 */
	gpio_free(timerdev.led_gpio);		
	cdev_del(&timerdev.cdev);/*  删除cdev */
	unregister_chrdev_region(timerdev.devid, TIMER_CNT); /* 注销设备号 */

	device_destroy(timerdev.class, timerdev.devid);
	class_destroy(timerdev.class);
}

module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python语言基础学习(下)

目录 一、顺序语句 二、条件语句 (1) if (2) if - else (3) if - elif - else 缩进和代码块 空语句 pass 三、循环语句 while 循环 for 循环 continue break 四、函数 创建函数 调用函数 函数返回 函数变量 函数递归 关键字参数 五、列表和元组 创建列表 …

CNCAP2024主动安全解析

一、新增场景 车辆自动紧急制动系统&#xff08;AEB C2C&#xff09;在 2021 版基础上新增了叉路口场景、高速公路追尾场景和 AEB 误作用场景&#xff1b;VRU 自动紧急制动&#xff08;AEB VRU&#xff09;试验在 2021 版基础上新增了交叉路口场景&#xff0c;同时对已有场景进…

你真的了解HTTPS协议吗

前言 在 HTTP 协议中有可能存在信息窃听或身份伪装等安全问题。使用 HTTPS 通信机制可以有效地防止这些问题。本文即将带大家来了解这些。 任何事物都有两面性&#xff0c;为了满足HTTP协议的快&#xff0c;但导致了它有如下的不足&#xff1a; 通信采用明文&#xff08;不加…

IDEA 2024.1安装与破解

一、下载 官网地址&#xff1a;https://www.jetbrains.com/idea/download/other.html 二、安装 傻瓜式安装即可 三、破解 3.1 破解程序 网站&#xff1a;https://3.jetbra.in/ 3.2 获取激活码 点击*号部分即可复制成功

深入解析RPC技术:原理、实现与应用

RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种计算机通信协议&#xff0c;允许一个程序&#xff08;客户端&#xff09;在本地调用另一个程序&#xff08;服务器&#xff09;中的函数或方法&#xff0c;并获取返回结果&#xff0c;就像调用…

Dubbo生态之sentinel限流

1. 限流算法 我们知道&#xff0c;在分布式架构中&#xff0c;当服务请求量过大时&#xff0c;容易对服务器造成不可预知的压力&#xff0c;因此&#xff0c;我们在客户端请求的时候&#xff0c;进行限流&#xff0c;起到一个保护的作用 常见的限流算法有: 计数器限流&#x…

猫头虎 解析:为什么AIGC在国内适合做TOB,在国外适合做TOC?

猫头虎 解析&#xff1a;为什么AIGC在国内适合做TOB&#xff0c;在国外适合做TOC&#xff1f; 博主 猫头虎 的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面…

Golang | Leetcode Golang题解之第114题二叉树展开为链表

题目&#xff1a; 题解&#xff1a; func flatten(root *TreeNode) {curr : rootfor curr ! nil {if curr.Left ! nil {next : curr.Leftpredecessor : nextfor predecessor.Right ! nil {predecessor predecessor.Right}predecessor.Right curr.Rightcurr.Left, curr.Righ…

python数据分析——apply 1

参考资料&#xff1a;活用pandas库 apply是指把函数同时作用于DataFrame的每一行或每一列。类似于编写一些跨每行或每列的for循环&#xff0c;并同时调用apply函数。 1、函数 函数是对python代码进行分组和复用的一种方法。如果某段代码会被多次使用&#xff0c;并且使用时是需…

【C++】——入门基础知识超详解

目录 ​编辑 1.C关键字 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 命名空间的使用有三种方式&#xff1a; 注意事项 3. C输入&输出 示例 1&#xff1a;基本输入输出 示例 2&#xff1a;读取多个值 示例 3&#xff1a;处理字符串输入 示例 4&#xff1a;读…

部署PIM-SM

拓扑图 配置 使能组播路由 配置OSPF 组播路由器接口配置pim-sm 连接组成员的接口使能igmp pim路由器上配置静态RP sysname AR1 # multicast routing-enable # interface GigabitEthernet0/0/0ip address 10.1.12.1 255.255.255.0 pim sm # interface GigabitEthernet0/0/…

SpringBoot + MybatisPlus

SpringBoot MybatisPlus 整合记录 1. 硬件软件基本信息2. 相关链接3. 通过idea快速生成一个Springboot项目4. 启动报错问题解决问题一&#xff1a;Springboot启动的时候报错提示 “没有符合条件的Bean关于Mapper类型”问题二&#xff1a;启动的时候提示需要一个Bean&#xff0…

文件操作知识点

前言: 我们应该知道一般程序运行时产生的数据是存放在内存中的。但是如果程序关闭后这些内存就会被系统回收&#xff0c;如果内存内的有用的数据没有被保存下来&#xff0c;这些数据就丢失了。所以这个时候我们就可以使用磁盘来储存我们的数据。 目录 程序文件的分类 文件名…

第八篇【传奇开心果系列】Python微项目技术点案例示例:以微项目开发为案例,深度解读Dearpygui 编写图形化界面桌面程序的优势

传奇开心果博文系列 系列博文目录Python微项目技术点案例示例系列 博文目录前言一、开发图形化界面桌面程序的优势介绍二、跨平台特性示例代码和解析三、高性能特性示例代码和解析四、简单易用特性示例代码和解析五、扩展性强示例代码和解析六、现代化设计示例代码和解析七、知…

Android 快速调试网络 复制curl 到postMan

搜索这个插件 官网地址&#xff1a;https://github.com/itkacher/OkHttpProfiler 集成教程也在里面集成完毕后右下角有一个入口点进去可以复制curl| 插件名称&#xff1a;Okhttp Profiler 真的很好用&#xff01;

软测刷题-错题1

提高测试效率的方法&#xff1a; 1、不要做无效的测试 2.不要做重复的测试 3.不同测试版本的测试侧重点 4.优化测试顺序 LoadRunner是对服务器进行施压。 在数据库中存在的用户数是指注册用户数。 input标签可以直接使用send_keys实现上传&#xff0c;而非input标签是无法直…

指纹识别系统架构

目录 1. 系统架构 1.1 指纹采集模块 1.2 指纹处理模块 1.3 指纹登记模块 1.4 指纹识别模块 1.5 指纹识别决策模块 1.6 管理模块 1.6.1 存储管理 1.6.2 传输管理 1.6.3 安全管理 1.7 应用开放功能 1.7.1 指纹登记功能 1.7.2 指纹验证功能 1.7.3 指纹辨识功能 2. …

SparkStreaming架构原理(详解)

Spark概述 SparkStreaming架构原理 Spark Streaming的架构主要由以下几个关键部分组成。 1.数据源接收器&#xff08;Receiver&#xff09; 执行流程开始于数据源接收阶段&#xff0c;其中接收器&#xff08;Receiver&#xff09;负责从外部数据源获取数据流。 接收器可以连…

Android Studio开发之路(十四)自定义Titlebar以及设置顶部状态栏颜色

一、描述 项目需求&#xff0c;我要做一个下图这样的titlebar,包括一个返回按钮&#xff0c;一个关闭按钮&#xff0c;一个文本框。默认的titlebar按钮设计不太满足我的需求&#xff0c;于是我打算自定义一个titlebar组件&#xff0c;应用到我的每一个页面 二、titlebar组件设…

flink程序本地运行:No ExecutorFactory found to execute the application

1.问题描述 在idea中运行flink job程序出现如下错误&#xff1a; Exception in thread "main" java.lang.IllegalStateException: No ExecutorFactory found to execute the application. at org.apache.flink.core.execution.DefaultExecutorServiceLoader.getE…