设备驱动-【转载】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循环。