Linux時間子系統之七:定時器的應用--msleep(),hrtimer_nanosleep()
我們已經在前面幾章介紹了低解析度定時器和高精度定時器的實現原理,核心為了方便其它子系統,在時間子系統中提供了一些用於延時或排程的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 | 毫秒 | 未完成的毫秒數 | 是 |
圖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;
}
接著我們看看do_nanosleep的實現程式碼,它首先通過hrtimer_init_sleeper函式,把定時器的回撥函式設定為hrtimer_wakeup,把當前程式的task_struct結構指標儲存在hrtimer_sleeper結構的task欄位中: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/while迴圈內:啟動定時器,掛起當前程式,等待定時器或其它事件喚醒程式。這裡的迴圈體實現比較怪異,它使用hrtimer_active函式間接地判斷定時器是否到期,如果hrtimer_active返回false,說明定時器已經過期,然後把hrtimer_sleeper結構的task欄位設定為NULL,從而導致迴圈體的結束,另一個結束條件是當前程式收到了訊號事件,所以,當因為是定時器到期而退出時,do_nanosleep返回true,否則返回false,上述的hrtimer_nanosleep正是利用了這一特性來決定它的返回值。以下是do_nanosleep迴圈體的程式碼: 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;
}
除了hrtimer_nanosleep,高精度定時器系統還提供了幾種用於延時/掛起程式的api:- schedule_hrtimeout 使得當前程式休眠指定的時間,使用CLOCK_MONOTONIC計時系統;
- schedule_hrtimeout_range 使得當前程式休眠指定的時間範圍,使用CLOCK_MONOTONIC計時系統;
- schedule_hrtimeout_range_clock 使得當前程式休眠指定的時間範圍,可以自行指定計時系統;
- usleep_range 使得當前程式休眠指定的微妙數,使用CLOCK_MONOTONIC計時系統;
相關文章
- Linux時間子系統之七:定時器的應用--msleep(),hrtimer_nanosleep()【轉】Linux定時器NaN
- linux時間子系統(三)Linux
- linux下的系統時間、硬體時間設定Linux
- linux系統時間設定Linux
- Linux設定系統時間Linux
- Linux時間設定系統時間、硬體時間和時間服務Linux
- 安卓應用修改系統時間安卓
- 雲伺服器Linux系統設定時間同步設定伺服器Linux
- Linux系統時間與RTC設定Linux
- jmeter(七)定時器JMeter定時器
- linux 如何建立定時任務?crontab -e 定時任務使用的時間是系統時間Linux
- Linux時間子系統之八:動態時鐘框架(CONFIG_NO_HZ、tickless)Linux框架
- Linux系統管理之定時任務Linux
- Linux 系統時間和硬體時間Linux
- Linux應用層的定時器TimerLinux定時器
- 定時器時間資料轉換子程式分析 (轉)定時器
- linux 延時函式 msleep() 與mdelay()Linux函式
- Linux時間子系統之八:動態時鐘框架(CONFIG_NO_HZ、tickless)【轉】Linux框架
- linux學習筆記之小談時鐘時間,使用者CPU時間,系統CPU時間 .Linux筆記
- Linux系統如何更改時間時區Linux
- 修改linux系統時間Linux
- linux同步系統時間Linux
- linux修改系統時間Linux
- linux 修改系統時間Linux
- 【原創】解BUG-xenomai核心與linux核心時間子系統之間存在漂移AILinux
- 通過Android應用更改系統時間Android
- linux系統程式設計之檔案與IO(七):時間函式小結Linux程式設計函式
- 2.系統定時器定時器
- Linux如何檢視系統/伺服器的執行時間及啟動時間?Linux伺服器
- linux系統程式設計之訊號(八):三種時間結構及定時器setitimer()詳解Linux程式設計定時器
- 調整linux系統時間和時區Linux
- Linux調整系統時間和時區的方法Linux
- Linux系統時間同步方法。Linux
- linux調整系統時間Linux
- 北斗授時伺服器(時鐘系統)在政務系統應用方案伺服器
- 【Linux-時間設定】-設定時區並調整時間Linux
- Centos系統伺服器設定時間自動同步!CentOS伺服器
- linux系統時區設定Linux