轉自:http://blog.csdn.net/droidphone/article/details/8104433
我們已經在前面幾章介紹了低解析度定時器和高精度定時器的實現原理,核心為了方便其它子系統,在時間子系統中提供了一些用於延時或排程的API,例如msleep,hrtimer_nanosleep等等,這些API基於低解析度定時器或高精度定時器來實現,本章的內容就是討論這些方便、好用的API是如何利用定時器系統來完成所需的功能的。
/*****************************************************************************************************/
宣告:本博內容均由http://blog.csdn.net/droidphone原創,轉載請註明出處,謝謝!
/*****************************************************************************************************/
1. msleep
msleep相信大家都用過,它可能是核心用使用最廣泛的延時函式之一,它會使當前程式被排程並讓出cpu一段時間,因為這一特性,它不能用於中斷上下文,只能用於程式上下文中。要想在中斷上下文中使用延時函式,請使用會阻塞cpu的無排程版本mdelay。msleep的函式原型如下:
- void msleep(unsigned int msecs)
延時的時間由引數msecs指定,單位是毫秒,事實上,msleep的實現基於低解析度定時器,所以msleep的實際精度只能也是1/HZ級別。核心還提供了另一個比較類似的延時函式msleep_interruptible:
- unsigned long msleep_interruptible(unsigned int msecs)
延時的單位同樣毫秒數,它們的區別如下:
函式 | 延時單位 | 返回值 | 是否可被訊號中斷 |
---|---|---|---|
msleep | 毫秒 | 無 | 否 |
msleep_interruptible | 毫秒 | 未完成的毫秒數 | 是 |
最主要的區別就是msleep會保證所需的延時一定會被執行完,而msleep_interruptible則可以在延時進行到一半時被訊號打斷而退出延時,剩餘的延時數則通過返回值返回。兩個函式最終的程式碼都會到達schedule_timeout函式,它們的呼叫序列如下圖所示:
圖1.1 兩個延時函式的呼叫序列
下面我們看看schedule_timeout函式的實現,函式首先處理兩種特殊情況,一種是傳入的延時jiffies數是個負數,則列印一句警告資訊,然後馬上返回,另一種是延時jiffies數是MAX_SCHEDULE_TIMEOUT,表明需要一直延時,直接執行排程即可:
- signed long __sched schedule_timeout(signed long timeout)
- {
- struct timer_list timer;
- unsigned long expire;
- switch (timeout)
- {
- case MAX_SCHEDULE_TIMEOUT:
- schedule();
- goto out;
- default:
- if (timeout < 0) {
- printk(KERN_ERR "schedule_timeout: wrong timeout "
- "value %lx\n", timeout);
- dump_stack();
- current->state = TASK_RUNNING;
- goto out;
- }
- }
然後計算到期的jiffies數,並在堆疊上建立一個低解析度定時器,把到期時間設定到該定時器中,啟動定時器後,通過schedule把當前程式排程出cpu的執行佇列:
- expire = timeout + jiffies;
- setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
- __mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
- schedule();
到這個時候,程式已經被排程走,那它如何返回繼續執行?我們看到定時器的到期回撥函式是process_timeout,引數是當前程式的task_struct指標,看看它的實現:
- static void process_timeout(unsigned long __data)
- {
- wake_up_process((struct task_struct *)__data);
- }
噢,沒錯,定時器一旦到期,程式會被喚醒並繼續執行:
- del_singleshot_timer_sync(&timer);
- /* Remove the timer from the object tracker */
- destroy_timer_on_stack(&timer);
- timeout = expire - jiffies;
- out:
- return timeout < 0 ? 0 : timeout;
- }
schedule返回後,說明要不就是定時器到期,要不就是因為其它時間導致程式被喚醒,函式要做的就是刪除在堆疊上建立的定時器,返回剩餘未完成的jiffies數。
說完了關鍵的schedule_timeout函式,我們看看msleep如何實現:
- signed long __sched schedule_timeout_uninterruptible(signed long timeout)
- {
- __set_current_state(TASK_UNINTERRUPTIBLE);
- return schedule_timeout(timeout);
- }
- void msleep(unsigned int msecs)
- {
- unsigned long timeout = msecs_to_jiffies(msecs) + 1;
- while (timeout)
- timeout = schedule_timeout_uninterruptible(timeout);
- }
msleep先是把毫秒轉換為jiffies數,通過一個while迴圈保證所有的延時被執行完畢,延時操作通過schedule_timeout_uninterruptible函式完成,它僅僅是在把程式的狀態修改為TASK_UNINTERRUPTIBLE後,呼叫上述的schedule_timeout來完成具體的延時操作,TASK_UNINTERRUPTIBLE狀態保證了msleep不會被訊號喚醒,也就意味著在msleep期間,程式不能被kill掉。
看看msleep_interruptible的實現:
- signed long __sched schedule_timeout_interruptible(signed long timeout)
- {
- __set_current_state(TASK_INTERRUPTIBLE);
- return schedule_timeout(timeout);
- }
- unsigned long msleep_interruptible(unsigned int msecs)
- {
- unsigned long timeout = msecs_to_jiffies(msecs) + 1;
- while (timeout && !signal_pending(current))
- timeout = schedule_timeout_interruptible(timeout);
- return jiffies_to_msecs(timeout);
- }
msleep_interruptible通過schedule_timeout_interruptible中轉,schedule_timeout_interruptible的唯一區別就是把程式的狀態設定為了TASK_INTERRUPTIBLE,說明在延時期間有訊號通知,while迴圈會馬上終止,剩餘的jiffies數被轉換成毫秒返回。實際上,你也可以利用schedule_timeout_interruptible或schedule_timeout_uninterruptible構造自己的延時函式,同時,核心還提供了另外一個類似的函式,不用我解釋,看程式碼就知道它的用意了:
- signed long __sched schedule_timeout_killable(signed long timeout)
- {
- __set_current_state(TASK_KILLABLE);
- return schedule_timeout(timeout);
- }
2. hrtimer_nanosleep
- long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
- const enum hrtimer_mode mode, const clockid_t clockid)
- {
- struct restart_block *restart;
- struct hrtimer_sleeper t;
- int ret = 0;
- unsigned long slack;
- slack = current->timer_slack_ns;
- if (rt_task(current))
- slack = 0;
- hrtimer_init_on_stack(&t.timer, clockid, mode);
- hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack);
- if (do_nanosleep(&t, mode))
- goto out;
- /* Absolute timers do not update the rmtp value and restart: */
- if (mode == HRTIMER_MODE_ABS) {
- ret = -ERESTARTNOHAND;
- goto out;
- }
- if (rmtp) {
- ret = update_rmtp(&t.timer, rmtp);
- if (ret <= 0)
- goto out;
- }
- restart = ¤t_thread_info()->restart_block;
- restart->fn = hrtimer_nanosleep_restart;
- restart->nanosleep.clockid = t.timer.base->clockid;
- restart->nanosleep.rmtp = rmtp;
- restart->nanosleep.expires = hrtimer_get_expires_tv64(&t.timer);
- ret = -ERESTART_RESTARTBLOCK;
- out:
- destroy_hrtimer_on_stack(&t.timer);
- return ret;
- }
- void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
- {
- sl->timer.function = hrtimer_wakeup;
- sl->task = task;
- }
- EXPORT_SYMBOL_GPL(hrtimer_init_sleeper);
- static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)
- {
- hrtimer_init_sleeper(t, current);
- do {
- set_current_state(TASK_INTERRUPTIBLE);
- hrtimer_start_expires(&t->timer, mode);
- if (!hrtimer_active(&t->timer))
- t->task = NULL;
- if (likely(t->task))
- schedule();
- hrtimer_cancel(&t->timer);
- mode = HRTIMER_MODE_ABS;
- } while (t->task && !signal_pending(current));
- __set_current_state(TASK_RUNNING);
- return t->task == NULL;
- }
- schedule_hrtimeout 使得當前程式休眠指定的時間,使用CLOCK_MONOTONIC計時系統;
- schedule_hrtimeout_range 使得當前程式休眠指定的時間範圍,使用CLOCK_MONOTONIC計時系統;
- schedule_hrtimeout_range_clock 使得當前程式休眠指定的時間範圍,可以自行指定計時系統;
- usleep_range 使得當前程式休眠指定的微妙數,使用CLOCK_MONOTONIC計時系統;