裝置驅動-【轉載】linux核心執行緒睡眠與喚醒
版權宣告:本文為CSDN博主「luckywang1103」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/luckywang1103/article/details/47859995
這裡以核心usb gadget driver中f_mass_storage驅動為例子進行說明。
static int sleep_thread(struct fsg_common *common) { int rc = 0; /* Wait until a signal arrives or we are woken up */ for (;;) { try_to_freeze(); set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) { rc = -EINTR; break; } if (common->thread_wakeup_needed) break; schedule(); } __set_current_state(TASK_RUNNING); common->thread_wakeup_needed = 0; smp_rmb(); /* ensure the latest bh->state is visible */ return rc; }
try_to_freeze()
try_to_freeze()函式需要參考Documentation/power/freezing-of-tasks.txt
這裡也有這個文件對應的中文翻譯:http://blog.csdn.net/arethe/article/details/6069358
核心為每個程序在適當的時候呼叫try_to_freeze來設定PF_FREEZE標誌,當系統要suspend的時候,
系統那邊會呼叫freeze_process函式來將所有可冷凍的任務的TIF_FREEZE標誌置位,然後所有有TIF_FREEZE標誌的程序會睡眠。
當系統從suspend狀態恢復的時候呼叫thaw_process函式來清除所有冷凍任務的PF_FREEZE標誌。
核心執行緒睡眠的方式(參考了LDD3這本書 P156)
方式1
step1:通過改變當前的狀態,改變了處理器處理該程序的方式,但尚未使程序讓出處理器。
set_current_state(TASK_INTERRUPTIBLE)
step2:檢查睡眠等待的條件,如果沒有任何執行緒試圖喚醒這個執行緒,那麼這個執行緒可以進行睡眠;否則如果不檢查睡眠等待的條件而直接進行睡眠,而這個時候有執行緒試圖喚醒這個執行緒,那麼很容易失去被喚醒的機會。
if (!condition)
schedule();
step3:執行緒通過上面的步驟一直處在睡眠狀態,當有別的執行緒將睡眠的執行緒喚醒之後,需要執行:
set_current_state(TASK_RUNNING)
方式2
step1:建立並初始化一個等待佇列入口
DEFINE_WAIT(my_wait)
step2:將我們的等待佇列入口新增到佇列中,並設定程序的狀態
prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);
step3:
if (!condition)
schedule();
step4:執行緒通過上面的步驟一直處在睡眠狀態,當有別的執行緒將睡眠的執行緒喚醒之後,需要執行:
void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);
可以看出開頭貼出的程式碼睡眠採用了”方式2”,另外以上程式碼中需要包含標頭檔案< linux/sched.h>
喚醒執行緒的方法
方式1:通過wake_up_process來喚醒睡眠的執行緒
static void wakeup_thread(struct fsg_common *common)
{
smp_wmb(); /* ensure the write of bh->state is complete */
/* Tell the main thread that something has happened */
common->thread_wakeup_needed = 1;
if (common->thread_task)
wake_up_process(common->thread_task);
}
對於開頭給出的程式碼,由於睡眠的執行緒收到wake_up_process(common->thread_task),於是便從schedule()函式中退出,繼續在for迴圈中,由於此時if (common->thread_wakeup_needed)成立,所以就此退出了for迴圈。
方式2:通過傳送訊號來喚醒睡眠的執行緒
static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
{
unsigned long flags;
/*
* Do nothing if a higher-priority exception is already in progress.
* If a lower-or-equal priority exception is in progress, preempt it
* and notify the main thread by sending it a signal.
*/
spin_lock_irqsave(&common->lock, flags);
if (common->state <= new_state) {
common->exception_req_tag = common->ep0_req_tag;
common->state = new_state;
if (common->thread_task)
send_sig_info(SIGUSR1, SEND_SIG_FORCED,
common->thread_task);
}
spin_unlock_irqrestore(&common->lock, flags);
}
對於開頭給出的程式碼,由於signal_pending(current)函式相當於是核心安裝的訊號處理函式,現在由於收到了訊號,也就退出了for迴圈。