ttwu(try to wake up)是Linux内核中一条热点路径,在多任务同步,唤醒操作中起到关键作用,这里简单分析一下磁盘读写过程的TTWU执行逻辑。
以ext文件系统扇区读为例,提交读请求后,进入到wait_on_buffer队列
异步读,磁盘读过程结束后通过b_end_io回调通知等待任务
具体的说是通过unlock_buffer唤醒等待任务
等待队列,以&bh->b_state虚拟地址和BH_Lock位于域索引wait_queue_head等待队列头节点
b_state成员只是一个普通结构体成员
等待基础设施,wake_bit_function是唤醒函数:
等待队列哈希桶定义,cacheline对齐,flags,bit_nr表示唤醒条件
等待过程:
等待在前面提到的散列头:
action 是bit_wait_io
wq_queue->lock的使用
这里有一个问题,虽然首先加入队列,然后才进行条件测试可以保证不会出现 测试和入队两个操作之间被打断原子性操作的问题,但是这里会引入另一个问题,由于没有锁保护,条件判断和__schedule除队列操作之前不是原子的,如果中间插入了b_end_io中断唤醒任务并且成功执行,action表示的调度路径__schedule还没有执行,会不会有问题?
__schedule会根据当前状态进行除队列操作,如果当前状态是task_unterruptable,则调度器会将当前任务从就绪队列删除
答案是不会,即便在执行action表示的调度路径之前,由于没有锁保护发生了中断唤醒重入,再次执行action,io_schedule, __schdule时,任务的state已经发生了变化,将不会走进dequeue的分支将任务出队.
wakeup操作会将任务状态从Taskinterruptable变为TASK_RUNNING!!!!!!!!!!!!!!
唤醒过程:
核心函数是unlock_buffer.
根据buffer_head的b_state成员虚拟地址和位域,找到睡眠队列头然后进行唤醒
队列头哈希桶内可能有多个睡眠节点,根据KEY找到我们要唤醒的等待此BH的任务
唤醒操作入口:wake_bit_function.
终于看到了TTWU唤醒操作,如上的ttwu_queue
持有readqueue大锁,和__schedule里面判断state时持有统一把大锁,唤醒操作在这里面ttwu_do_activate
最终,我们找到了改变任务状态的地方,在前面介绍的CASE中,如果中断提前到来,将会走到这里,将任务设置为运行状态。而对应在schedule函数中,这种情况下将不会进行实际的调度操作。唤醒仍然成功。
TASK_RUNNING 0,妈妈再也不用担心我会进到这里来了!!!也就是说corner case中的介入唤醒操作是生效的,唤醒后即便再执行schedule操作,当前任务仍然是running状态,顺便说一句,Linux是允许许多任务同时在RUNNING状态的,至少有IDLE不是么.linux的 running相当于 rtt的ready+running不是么?
所以,最后的finish_wait操作设置task_running状态是多余的?
也不能这么说,如果唤醒是通过正常的wake_up过程唤醒,调用链里面一定会调用try_to_wake_up函数,最终会设置p->state为running.但是并不是所有的情况下都需要睡眠的,比如,有些使用场景下,调用prepare_to_wait之后,是否要执行睡眠还需要看条件,如果条件不满足,没有真正执行休眠,最后需要执行finish_wait来设置task的state为running.