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

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会在很短的时间段内很快执行,并以原子模式执行

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值