天天看點

等待隊列——休眠與喚醒

核心中的休眠是通過等待隊列來處理的。等待隊列是一個由正在等待某個事件發生的程序組成的一個簡單連結清單。在核心用wait_queue_head_t來表示。

定義:

DECLARE_WAITQUEUE()(靜态定義)

init_waitqueue_head()(動态定義)

在核心中實作休眠的方法有點複雜,實作的模闆如下:

DEFINE_WAIT(wait);
add_wait_queue(q,&wait);//這個函數調用是可選
while(!condition){
prepare_to_wait(&q,&wait,TASK_INTERRUPTIBLE);
if(signal_pending(current))
schedule();
}
finish_wait(&q,&wait);

一個程序執行如下步驟将自己加入到一個等待隊列中:

  • 通過宏DEFINE_WAIT()來建立一個等待隊列項。
  • 通過函數add_wait_queue()将該項加入到一個等待隊列中。當等待的事件(條件)為真時,等待隊列會喚醒該程序項。當然,需要在其他地方調用wake_up()函數。
  • 調用prepare_to_wait()函數将程序的狀态改為TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE。該函數也會在必要的時候将程序加回到等待隊列中,在後續的疊代中會用到(提示:第二個步驟可選,因為該函數在任務清單為空的時候也會将目前任務項加入到等待隊列中)。
  • 如果狀态設定為TASK_INTERRUPTIBLE,,一個信号會喚醒該程序。這稱為僞休眠。是以要檢測和處理信号。
  • 當程序被喚醒,它再次檢測條件是否為真。如果為真,它會退出循環。否則,再次調用schedule()然後重複上述過程。
  • 當條件為真,該程序會将其狀态設為TASK_RUNNING并将自己通過finish_wait()從等待隊列中删除。

一個例子:

staticssize_t inotify_read(struct file *file, char __user *buf,

size_t count, loff_t *pos)

{

structfsnotify_group *group;

structfsnotify_event *kevent;

char__user *start;

intret;

DEFINE_WAIT(wait);

start= buf;

group= file->private_data;

while(1) {

prepare_to_wait(&group->notification_waitq,&wait,TASK_INTERRUPTIBLE);

mutex_lock(&group->notification_mutex);

kevent= get_one_event(group, count);

mutex_unlock(&group->notification_mutex);

if(kevent) {

ret= PTR_ERR(kevent);

if(IS_ERR(kevent))

break;

ret= copy_event_to_user(group, kevent, buf);

fsnotify_put_event(kevent);

if(ret < 0)

break;

buf+= ret;

count-= ret;

continue;

}

ret= -EAGAIN;

if(file->f_flags & O_NONBLOCK)

break;

ret= -EINTR;

if(signal_pending(current))

break;

if(start != buf)

break;

schedule();

}

finish_wait(&group->notification_waitq,&wait);

if(start != buf && ret != -EFAULT)

ret= buf - start;

returnret;

}

另一種模闆:

staticint

my_thread(void*unused)

{

DECLARE_WAITQUEUE(wait,current);

daemonize("my_thread");

add_wait_queue(&my_thread_wait,&wait);

while(1) {

set_current_state(TASK_INTERRUPTIBLE);

if(signal_pending(current))

schedule();

if(pink_slip) {

break;

}

}

__set_current_state(TASK_RUNNING);

remove_wait_queue(&my_thread_wait,&wait);

complete_and_exit(&my_thread_exit,0);

}

喚醒

通過函數wake_up()喚醒,它将喚醒所有在特定等待隊列上等待的程序。一般情況下預設的喚醒函數為:default_wake_function()。它會調用try_to_wake_up(),将被喚醒的程序狀态設定為TASK_RUNNING,然後調用enqueue_task()将該程序加入到紅黑樹中,如果被喚醒的程序的優先級大于目前程序的優先級,設定need_resched為1。休眠與喚醒之間的關系如下:

等待隊列——休眠與喚醒

\

僞喚醒是指程序是因為接收到某個信号而被喚醒,而不是等待事件發生而導緻其被喚醒。

在最新的核心代碼中,一般會使用更高層的接口:wait_event或wait_event_timeout接口。使用wake_up_all喚醒所有添加到某個等待隊列連結清單中的等待隊列。使用模闆如下:

1. 初始化一個等待隊列頭:

  init_waitqueue_head(&ret->wait_queue);

  注: 判斷隊列是否為空: waitqueue_active(...), 傳回false即表示隊列為空.

2. 等待某個條件發生:

   wait_event(...) 或 wait_event_timeout(...)

3. 喚醒隊列

   wake_up_all(...)