内核中的休眠是通过等待队列来处理的。等待队列是一个由正在等待某个事件发生的进程组成的一个简单链表。在内核用wait_queue_head_t来表示。
定义:
DECLARE_WAITQUEUE()(静态定义)
或
init_waitqueue_head()(动态定义)
在内核中实现休眠的方法有点复杂,实现的模板如下:
/*‘q’isthewaitqueuewewishtosleepon*/ |
DEFINE_WAIT(wait); |
add_wait_queue(q,&wait);//这个函数调用是可选 |
while(!condition){/*conditionistheeventthatwearewaitingfor*/ |
prepare_to_wait(&q,&wait,TASK_INTERRUPTIBLE); |
if(signal_pending(current)) |
/*handlesignal*/ |
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; } |
另一种模板:
/*Helper thread */ staticint my_thread(void*unused) { /** * wait_queue_t wait; * init_waitqueue_entry(&wait, current); */ DECLARE_WAITQUEUE(wait,current);
daemonize("my_thread"); add_wait_queue(&my_thread_wait,&wait);
while(1) { /*Relinquish processor until event occurs */ set_current_state(TASK_INTERRUPTIBLE); if(signal_pending(current)) /*##handlesingalevent##*/ schedule(); /*Control gets here when the thread is woken upfrom the my_thread_wait wait queue */
/*Quit if let go */ if(pink_slip) { break; } /*Do the real work */ /*... */
}
/*Bail out of the wait queue */ __set_current_state(TASK_RUNNING); remove_wait_queue(&my_thread_wait,&wait);
/*Atomically signal completion and exit */ 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(...)
|