1. 程式人生 > 其它 >裝置驅動-【轉載】linux核心執行緒睡眠與喚醒

裝置驅動-【轉載】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迴圈。