Linux设备驱动程序学习笔记——第七章时间、延迟及延缓操作

news/2024/7/19 3:22:32

Linux设备驱动程序学习笔记

第七章时间、延迟及延缓操作

一、度量时间差
内核通过定时器中断来跟踪时间流
时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据HZ的值设定,HZ是一个与体系结构相关的常数,定义在<linux/param.h>,默认50-1200
每当时钟中断发生时,内核内部计时器的值就会+1。这个计数器是一个64位变量称为“jiffies_64”。驱动开发者通常访问jeffies变量,其unsigned long类型,要么与jiffies_64相同,要么为其低32位,其被视为只读变量。

(1)使用jiffies计数器
该计数器和读取工具在<linux/jiffies.h>中,但通常只需要包含<linux/sched.h>文件,后者会自动放入jiffies.h。

#include<linux/jiffies.h>
unsigned long j, stamp_1, stamp_half, stamp_n;
j = jiffies;//读取当前值
stamp_1 = j + HZ;//未来1秒
stamp_half = j + HZ/2;//半秒
stamp_n = j + n * HZ/1000;//n毫秒

用户空间的时间表述方法(struct timeval和struct timespec)与内核表述方法之间的转换
unsigned long timespec_to_jiffies(struct timespec *value);
void jiffies_to_timespec(unsigned long jiffies, struct timespec *value);
unsigned long timeval_to_jiffies(struct timeval *value);
void jiffies_to_timeval(unsigned long jiffies, struct timeval *value);

(2)处理器特定的寄存器
如果需要度量时间非常短或者需要极高的时间精度,就可以使用与特定平台相关的资源。
关于时间戳计数器,在SMP系统中,他们不会在多个处理器间保持同步。为了确保获得一致的值,我们需要为查询该计数器的代码禁止抢占

二、获取当前时间
墙钟时间指日常生活使用的时间,用年月日表达
内核提供了将墙钟时间转换为jiffies值的函数

#include<linux/time.h>
unsigned long mktime(unsigned int year, unsigned int mom. 
					unsigned int day, unsigned int hour,
					unsigned int min, unsigned int sec);

三、延迟执行
长延时
(1)忙等待
最简单,但也不推荐

while(time_before(jiffies, j1))
	cpu_relax();

会占用处理器,忙等待,严重降低效率

(2)让出处理器

while(time_before(jiffies, j1))
	schedule();//调度函数

不安全,依然会影响整体性能

(3)超时

#include<wait>
long wait_event_timeout(wait_queue_head_t q, condition, long timeout);
long wait_event_interruptible_timeout(wait_queue_head_t q, condition, long timeout);//这里的timeout表示要等待的jiffies值

//方法1:
wait_queue_head_t wait;
init_waitqueue_head(&wait);
wait_event_interruptible_timeout(wait, 0, delay);

//方法2,因为不会出现唤醒wake_up,即进程始终都会是超时到期被唤醒,
//内核为我们提供了schedule_timeout函数,避免定义多余的等待队列头:
#include <linux/sched.h>
signed long schedule_timeout(signed long timeout);						

set_current_state(TASK_INTERRUPTIBLE);//需要提前声明,否则和schedule的调用一样
schedule_timeout(delay);//超时到期和进程被真正调度执行之间,需要额外的时间,即实际会比所请求的延时更长一点

短延迟

#include<linux/delay.h>
/*以下三种均为忙等待函数*/
void ndelay(unsigned long nsece);//纳秒级
void udelay(unsigned long usece);//微秒级
void mdelay(unsigned long msece);//毫秒级

/*实现毫秒级或者更长的延迟还有另一种方法,其不涉及忙等待*/
void msleep(unsigned int millisecs);//不可中断,毫秒
unsigned long msleep_interruptible(unsigned int millisecs);//如果希望有唤醒能打断等待,选用。如果被提前唤醒,返回剩余毫秒数
void ssleep(unsigned int seconds);//进入不可中断休眠,休眠以秒计数

四、内核定时器
内核定时器可用来在未来的某个特定时间点(基于时钟滴答)调度执行某个函数。
一个内核定时器是一个数据结构,他告诉内核在用户定义的时间点使用用户定义的参数来执行一个用户定义的函数。其实现位于<linux/timer.h>和kernel/timer.c中,将在"内核定时器实现"一节中详细描述。
中断上下文中必须遵守:

  1. 不允许访问用户空间
  2. current指针在原子模式没有任何意义
  3. 不能执行休眠或调度
    #include<asm/hardirq.h>
    in_interrupt()判断是否正运行于中断上下文,如果在,返回非零值,而无论是硬件中断还是软件中断。
    in_atomic(),当调度不允许时,返回非零值。调度不允许的情况包括硬件和软件上下文中断以及拥有自旋锁的任何时间点

(1)定时器API
内核为驱动程序提供了一组用来声明、注册、删除内核定时器的函数

#include<linux/timer.h>
struct timer_list{
	/*...*/
	//expires表示期望定时器执行的jiffies值,到达该值后调用function,参数是data,
	//data可为指针
	unsigned long expires;
	void (*function)(unsigned long);
	unsigned long data;
};

void init_timer(struct timer_list *timer);
struct timer_list TIMER_INITIALIZER(_function, _expires, _data);

void add_timer(struct timer_list *timer);
void del_timer(struct timer_list *timer);
int mod_timer(struct timer_list *timer, unsigned long expires);//重置某定时器的到期时间
int del_timer_sync(struct timer_list *timer);
int timer_pending(const struct timer_list *timer);//通过读取一个不可见字段,返回定时器是否正在被调度运行

(2)内核定时器的实现
可做了解,不在记录

五、tasklet

#include<linux/interrupt.h>
struct tasklet_struct{
	void (*func)(unsigned long);
	unsigned long data;
}
void tasklet_init(struct tasklet_struct *t,
			void (*func)(unsigned long), unsigned long data);
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);

六、工作队列
tasklet代码必须是原子的,workqueue可以休眠
tasklet始终运行在被初始提交的同一处理器上,但这只是工作队列的默认方式。
内核代码可以请求工作队列函数的执行延迟给定的时间间隔
tasklet会在很短的时间段内很快执行,并以原子模式执行

七、共享队列
设备驱动程序不需要有自己的工作队列,或者只是偶尔需要向队列提交任务,可以选用更简单、更有效的方法是使用内核提供的共享的默认工作队列。


http://www.niftyadmin.cn/n/3172421.html

相关文章

Linux设备驱动程序学习笔记——第八章分配内存

Linux设备驱动程序学习笔记 第八章分配内存 一、kmalloc函数的内幕 &#xff08;1&#xff09;flags参数 //kmalloc原型 #include<linux/slab.h> void *kmalloc(size_t size, int flags);//flags分配标志&#xff0c;最常用的是GFP_FERNEL分配标志&#xff1a; GFP_A…

Django 分组查询与ordering字段 巨坑

样例&#xff1a; class Test(Model):class Meta:db_table testordering [字段1&#xff0c; 字段2&#xff0c; 字段3]django里常见的 group by 查询写法 : Test.objects.values(查询字段).filter(过滤条件).annotate(分组字段)遇到的问题&#xff1a; 这个分组查询的巨坑…

MyBatis使用数组作为参数的配置方法

2019独角兽企业重金招聘Python工程师标准>>> 传入的参数是String[] arr; resultMap的值是resultMap的id,也是定义了java属性和表字段对应关系的配置的id collection的值必须为array item是指遍历时的别名, index即下标 <!-- 使用数组为参数查出所有子代理商的订单…

Linux设备驱动程序学习笔记——第九章 与硬件通信

Linux设备驱动程序学习笔记 第九章 与硬件通信 一、I/O端口和I/O内存 每种外设都通过读写寄存器进行控制。 &#xff08;1&#xff09;I/O寄存器和常规内存 注意避免由于CPU或编译器不恰当的优化而改变预期的I/O动作。 有硬件缓存引起的问题很好解决&#xff0c;只要把底层硬…

安卓沉浸式状态栏

安卓沉浸式状态栏前言安卓版本Android4.4之前Android4.4&#xff08;API 19&#xff09; - Android 5.0&#xff08;API 21&#xff09;1.图片沉浸1.1 xml实现1.2代码实现2.ToolBar沉浸2.1xml实现2.1.1 设置 fitsSystemWindows 属性2.1.2 布局里添加占位状态栏2.2代码实现2.2.1…

深入理解Linux内核笔记——第三章进程

深入理解Linux内核笔记——第三章进程 进程 一、进程、轻量级进程和线程 &#xff08;1&#xff09;进程描述符 进程状态state 可运行状态TASK_RUNNING 可中断的等待状态TASK_INTERRUPTIBLE 不可中断的等待状态TASK_UNINTERRUPTIBLE 暂停状态TASK_STOPPED 跟踪状态TASK_TUACE…

Linux设备驱动程序学习笔记——第十章 中断处理

Linux设备驱动程序学习笔记 第十章 中断处理 一、安装中断处理例程 中断信号线是珍贵且有限的资源。内核维护了一个中断信号线的注册表&#xff0c;模块在使用中断前要现请求一个中断通道&#xff08;或者中断请求IRQ&#xff09;&#xff0c;然后在使用后释放该通道。 #inc…

Diagnostics: Failed to setup local dir D:/hadoop/tmp/nm-local-dir, which was marked as good.

最近在研究hadoop&#xff0c;想要运行hadoop自带的一个程序&#xff0c;可是缺一直出现一个问题&#xff1a; Job job_1491779488590_0002 failed with state FAILED due to: Application application_1491779488590_0002 failed 2 times due to AM Container for appattempt…