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 (&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: ldd , 内核 , 并发 , 延迟 , 时间
© 除非另有注明本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
© 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
© 转载请注明: LDD读书笔记第七章-时间、延迟及延缓操作 | 转自: Senghoo's Blog
© Tag: ldd , 内核 , 并发 , 延迟 , 时间