LDD读书笔记第七章-时间、延迟及延缓操作

度量时间差

在linux/param.h中定义HZ值,表示每秒发生时钟中断的频率。
内核时钟计数器:jiffies_64。
内核使用jiffies变量是unsigned log类型。
可能是jiffies_64的值也可能是其底32位。

使用jiffies计数器

头文件:linux/jiffies.h
jiffies变量被声明为volatile防止编译时优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*计算未来时间戳*/
j = jiffies;				/*当前值*/
stamp_1 = j + HZ;			/*1秒后*/
stamp_half = j + HZ/2;		/*半秒后*/
stamp_n = j + n * HZ / 1000;	/*n毫秒后*/
/*比较两个时间,为了避免溢出应使用宏避免直接比较*/
#include <linux/jiffies.h>
 
int time_after(unsigned long a, unsigned long b);
int time_before(unsigned long a, unsigned long b);
int time_after_eq(unsigned long a, unsigned long b);
int time_before_eq(unsigned long a, unsigned long b);
/*计算时间差*/
msec = diff * 1000 / HZ;
/*转换为毫秒值*/
#include <linux/time.h>
 
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);
/*读取jiffies_64*/
#include <linux/jiffies.h>
 
u64 get_jiffies_64(void);

处理器特定的寄存器

CPU 提供的时间戳寄存器。
通常和架构相关,仅需要特别高的精度的时候使用。

1
2
3
4
5
6
7
8
9
10
11
12
/*x86专用定义*/
rdtsc(low32,high32);
rdtscl(low32);
rdtscll(var64);
/*eg.*/
unsigned long ini, end;
rdtscl(ini); rdtscl(end);
printk("time lapse: %li\n", end - ini);
/*通用函数 在不支持的平台返回0*/
#include <linux/timex.h>
 
cycles_t get_cycles(void);

获取当前时间

获取墙钟时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*转换*/
#include <linux/time.h>
 
unsigned long mktime (unsigned int year, unsigned int mon,
				unsigned int day, unsigned int hour,
				unsigned int min, unsigned int sec);
/*获取当前时间*/
#include <linux/time.h>
 
void do_gettimeofday(struct timeval *tv);
/*或*/
#include  <linux/time.h>
 
struct timespec current_kernel_time(void);

延迟执行

长延迟

忙等待
忙等待会严重影响性能。
如果忙等待前禁止中断系统会挂掉。

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

让出处理器

1
2
3
while (time_before(jiffies, j1)) {
	schedule( );
}

超时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <linux/wait.h>
 
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);
/*用法*/
wait_queue_head_t wait;
init_waitqueue_head (&amp;wait);
wait_event_interruptible_timeout(wait, 0, delay);
/*专用延迟函数*/
#include <linux/sched.h>
 
signed long schedule_timeout(signed long timeout);
/*用法*/
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout (delay);

短延迟

1
2
3
4
5
6
7
8
#include <linux/delay.h>
 
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);

内核定时器

将来的某个时间点需要调度执行某个动作时使用。
线程在原子上下文。
原子上下文规则:
1、不允许访问用户空间
2、current指针无意义。
3、不能休眠或调度。
可以使用in_interrupt( ) 查看是否在中断上下文,如果在中断上下文返回非零值。
使用in_atomic( )查询是否可以发生调度。
这些函数在asm/hardirq.h头文件中。

定时器API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <linux/timer.h>
 
struct timer_list {
/* ... */
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);
int del_timer(struct timer_list * timer);
 
int mod_timer(struct timer_list *timer, unsigned long expires);
/*更新定时器时间*/
int del_timer_sync(struct timer_list *timer);
/*和del_timer相似但是返回时保证没有任何CPU在运行定时器函数*/
int timer_pending(const struct timer_list * timer);
/*定时器是否在运行*/
 
<h3>tasklet</h3>
 
 
希望稍候执行某个函数的时候使用。
设备驱动中适用于获取数据后调用tasklet来稍候处理数据。
特性;
1、一个tasklet可以在稍候禁用或重新启用,只有在禁用次数和启用次数相同时才调用
2、可以注册自身
3、高优先级执行
4、如果无高负荷tasklet可立即执行
5、可以和其他tasklet并发。
 
<pre lang="c">
#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);
/*其他函数*/
void tasklet_disable(struct tasklet_struct *t);
/*禁止指定的tasklet*/
void tasklet_disable_nosync(struct tasklet_struct *t);
/*禁止指定的tasklet,不会等待正在运行的tasklet,而且仍然可以在其他CPU执行*/
void tasklet_enable(struct tasklet_struct *t);
/*启用指定的tasklet*/
void tasklet_schedule(struct tasklet_struct *t);
/*调度执行某个tasklet*/
void tasklet_hi_schedule(struct tasklet_struct *t);
/*调度某个tasklet以高优先级*/
void tasklet_kill(struct tasklet_struct *t);
/*指定某个tasklet不会被在此调用。*/

工作队列

工作队列和 tasklet的区别。

1、tasklet在中断上下文所以代码必须是原子的,工作队列在特殊的内核进程上下文中,所以更灵活并且可休眠。

2、tasklet始终在同一cpu上运行,但是这只是工作队列的默认方式。

3、内核代码可以请求工作队列函数的执行延时给定的时间间隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*建立工作队列*/
struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);
/*初始化任务*/
/*编译时*/
DECLARE_WORK(name, void (*function)(void *), void *data);
/*运行时.INIT_WORK执行彻底的初始化,PREPARE_WORK不会初始化用来将work_struct链接到工作队列的指针。*/
INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);
/*提交到工作队列,delayed至少在delayed后执行任务。*/
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue,
	struct work_struct *work, unsigned long delay);
 
/*取消工作队列入口项*/
int cancel_delayed_work(struct work_struct *work);
void flush_workqueue(struct workqueue_struct *queue);
void destroy_workqueue(struct workqueue_struct *queue);
----------------- 全文结束 -----------------
© 该日志作者:Senghoo, 于2010年07月18日发表于分类"Linux Kernel"下;
© 除非另有注明本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
© 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
© 转载请注明: LDD读书笔记第七章-时间、延迟及延缓操作 | 转自: Senghoo's Blog
© Tag: , , , ,

你可以继续围观

  • 《深入理解linux内核》边读边说-内存寻址(一)
    内存地址 内存地址有三种类型:逻辑地址,线性地址,物理地址。 逻辑地址:常见程序所表示的地址就是逻辑地址。比如MS-DOS程序设计时使用DS:EAX,CS:IP等地址都是逻辑地址。此类地址把地址分为段偏移的模式来把程序分为若干段。在逻辑地址层面上可以把内存看为若干个段来分开的空间。 线性地址:...
  • LDD读书笔记第十一章-内核的数据类型
    Linux数据类型 Linux内核C语言数据类型大小   Arch     char     short     int     long &...

发表评论