linux內核線程睡眠與喚醒


這里以內核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循環。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com