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

 

posted @ 2022-04-17 19:02  张志伟122  阅读(389)  评论(0编辑  收藏  举报