《Linux Device Drivers》 第七章 時間、延時及延緩操作――note
來源:程序員人生 發(fā)布時間:2014-10-13 02:03:27 閱讀次數(shù):3081次
- 度量時間差
- 內(nèi)核通過定時器中斷來跟蹤時間流
- 時鐘中斷由系統(tǒng)定時硬件以周期性的間隔產(chǎn)生,這個間隔由內(nèi)核根據(jù)HZ的值設(shè)定,在常見的x86 PC平臺上,默認(rèn)定義為1000
- <linux/param.h>
- <linux/timex.h>
- jiffies_64
- unsigned long jiffies
- 使用jiffies計數(shù)器
- <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);
- 通常只需要包含<linux/sched.h>
- diff = (long)t2 -(long)t1
- msec = diff * 1000 / HZ
- <linux/times.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);
- u64 get_jiffies_64(void);
- <linux/types.h>
- proc/interrupts
- 處理器特定的寄存器
- 最有名的計數(shù)器寄存器就是TSC
- <asm/msr.h>
- rdtsc(low32, high32);
- rdtscl(low32);
- rdtscl1(var64);
- <linux/timex.h>
- cycles_t get_cycles(void);
- define rdtscl(dest) __asm__ __volatile__(“mfs0 %0,$9; nop” : “=r” (dest))
- 獲取當(dāng)前時間
- 內(nèi)核提供將墻鐘時間轉(zhuǎn)換為jiffies值的函數(shù)
- <linux/time.h>
- unsigned long mktime(unsigned int year, unsigned int mon, unsigned int day, unsigned int hour, unsigned int min, unsigned int sec);
- void do_gettimeofday(struct timeval *tv);
- struct timespec current_kernel_time(void);
- 延遲執(zhí)行
- 長延遲
- 忙等待
- while (time_before(jiffies, j1)) cpu_relax();
- 讓出處理器
- 在不需要CPU時主動釋放CPU
- <linux/sched.h>
- while (time_before(jiffies, j1)) schedule();
- 超時
- <linux/wait.h>
- long wait_event_timeout(wait_queue_head_t q, condition c, long timeout);
- long wait_event_interruptible_timeout(wait_queue_head_t q, condition c, long timeout);
- timeout值表示的是要等的jiffies值,而不是絕對時間值
- 如果超時到期,兩個函數(shù)返回0;如果進程由其他事件喚醒,則返回剩余的延遲實現(xiàn)
- <linux/sched.h>
- signed long schedule_timeout(signed long timeout);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(delay);
- 短延遲
- <linux/delay.h>
- void ndelay(unsigned long nsecs);
- void udelay(unsigned long usecs);
- void mdelay(unsigned long msecs);
- 這三個延遲函數(shù)均是忙等待函數(shù)
- unsigned long msleep_interruptible(unsigned int millisecs);
- void ssleep(unsigned int seconds);
- 內(nèi)核定時器
- 一個內(nèi)核定時器是一個數(shù)據(jù)結(jié)構(gòu),它告訴內(nèi)核在用戶定義的時間點使用用戶定義的參數(shù)來執(zhí)行一個用戶定義的函數(shù)
- 內(nèi)核定時器常常是作為“軟件中斷”的結(jié)果而運行的
- 如果處于進程上下文之外,則必須遵守如下規(guī)則
- 不允許訪問用戶空間
- current指針在原子模式下是沒有任何意義的,也是不可用的
- 不能執(zhí)行休眠或調(diào)度
- <asm/hardirq.h>
- in_interrupt()
- in_atomic()
- 任務(wù)可以將自己注冊以在稍后的時間重新運行
- 即使在單處理器系統(tǒng)上,定時器也會是競態(tài)的潛在來源
- 定時器API
- <linux/timer.h>
- struct timer_list
- unsigned long expires;
- void (*function)(unsigned long);
- unsigned long data;
- void init_timer(struct timer_list *time);
- struct timer_list TIMER_INITIALIZER(_function, _expires, _data);
- void add_timer(struct timer_list *timer);
- int del_timer(struct timer_list *timer);
- expires字段表示期望定時器執(zhí)行的jiffies值
- 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);
- 內(nèi)核定時器的實現(xiàn)
- 內(nèi)核定時器的實現(xiàn)要滿足如下需求及假定
- 定時器的管理必須盡可能做到輕量級
- 其設(shè)計必須在活動定時器大量增加時具有很好的伸縮性
- 大部分定時器會在最多幾秒或者幾分鐘內(nèi)到期,而很少存在長期延遲的定時器
- 定時器應(yīng)該在注冊它的同一CPU上運行
- 不管何時內(nèi)核代碼注冊了一個定時器,其操作最終會由internal_add_timer(定義在kernel/timer.c)執(zhí)行
- 級聯(lián)表的工作方式如下
- 如果定時器在接下來的0~255的jiffiew中到期,由該定時器就會被添加到256個鏈表中的一個(取決于expires字段的低8位值)
- 如果定時器在較遠(yuǎn)的未來到期(但在16384個jiffies之前),則該定時器會被添加到64個鏈表之一(取決于expires字段的9~14位)
- 對更遠(yuǎn)將來的定時器,相同的技巧用于15~20位、21~26位以及27~31位
- 如果定時器的expires字段代表了更遠(yuǎn)的未來,則利用延遲0xfffffff做散列運算,而在過去時間內(nèi)到期的定時器會在下一個定時器滴答時被調(diào)度
- 當(dāng)__run_times被激發(fā)時,它會執(zhí)行當(dāng)前定時器滴答上的所有掛起的定時器
- tasklet
- 中斷管理中大量使用了這種機制
- 始終在中斷期間運行,始終會在調(diào)度它的同一CPU運行,接收一個unsigned long參數(shù)
- 不能要求tasklet在某個給定時間執(zhí)行
- <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可以稍后被禁止或者重新啟用;只有啟用的次數(shù)和禁止的次數(shù)相同時,tasklet才會被執(zhí)行
- 和定時器類似,tasklet可以注冊自己本身
- tasklet可被調(diào)度以在通常的優(yōu)先級或者高優(yōu)先級執(zhí)行
- 如果系統(tǒng)負(fù)荷不重,則tasklet會立即得到執(zhí)行,但始終不會晚于下一個定時器滴答
- 一個tasklet可以和其他tasklet并發(fā),但對自身來講是嚴(yán)格串行處理的
- void tasklet_disable(struct tasklet_struct *t);
- void tasklet_disable_nosync(struct tasklet_struct *t);
- void tasklet_enable(struct tasklet_struct *t);
- void tasklet_schedule(struct tasklet_struct *t);
- void tasklet_hi_schedule(struct tasklet_struct *t);
- void tasklet_kill(struct tasklet_struct *t);
- tasklet的實現(xiàn)在kernel/softirq.c中
- 工作隊列
- 與tasklet區(qū)別
- tasklet在軟件中斷上下文中運行,因此,所有的tasklet代碼都必須是原子的。相反,工作隊列函數(shù)在一個特殊內(nèi)核進程的上下文中運行,因此它們具有更好的靈活性。尤其是,工作隊列函數(shù)可以休眠
- tasklet始終運行在被初始提交的同一處理器上,但這只是工作隊列的默認(rèn)方式
- 內(nèi)核代碼可以請求工作隊列函數(shù)的執(zhí)行延遲給定的時間間隔
- 工作隊列函數(shù)可具有更長的延遲并且不必原子化
- <linux/workqueue.h>
- struct workqueue_struct
- struct workqueue_struct *create_workqueue(const char *name);
- struct workqueue_struct *create_singlethread_workqueue(const char *name);
- struct work_struct
- DECLARE_WORK(name, void (*function)(void*), void *data);
- INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
- PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);
- 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);
- 以上兩個函數(shù)返回值為非零時意味著給定的work_struct結(jié)構(gòu)已經(jīng)等待在該隊列中
- int cancel_delayed_work(struct work_struct *work);
- 該入口項在開始執(zhí)行前被取消,則返回非零值
- void flush_workqueue(struct workqueue_struct *queue);
- void destroy_workqueue(struct workqueue_struct *queue);
- 共享隊列
- int schedule_work(struct work_struct *work);
- void flush_scheduled_work(void)
小結(jié):1. Linux基于時鐘中斷跟蹤,系統(tǒng)時間流。
2.定時器、任務(wù)必須遵循在原子上下文,定時器可以指定將來的調(diào)度時間,任務(wù)無法指定執(zhí)行的時間。
3.工作隊列,調(diào)度的函數(shù)可以休眠。
生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機掃描二維碼進行捐贈