什么是等待队列?
等待队列可以看作是双向链表,但队列头和队列成员不是同一种数据结构
上图中,能看出队列头包含一个自旋锁(用于互斥访问,保证安全)和两个指针,而等待队列的成员包含一个flags
域和一个task
域以及两个指针。
队列头可以看作是某一种资源,而队列的成员可以看作是请求这个资源而不得的人(进程)。
怎么用等待队列实现阻塞?
一个进程请求某项资源的过程(比如read
某个文件)如下:
1.用当前的进程描述块(PCB)初始化一个等待队列成员,表示一个等待任务,让这个新创建的队列成员的task
域指向当前的进程描述块。
2.请求的资源对应一个队列头(这里我不清楚这个队列头是谁创建的,也不知道这种对应关系是怎么存储的,但我觉得可以是红黑树或者什么东西,然后请求资源的进程去红黑树里找,如果没找到就自己创建并且插入,反正应该可以实现),然后将上面创建的队列成员插入这个队列头所在的队列。这里的插入要先获得队列头的自旋锁。
同时,要将当前进程标记为TASK_UNINTERRUPTIBLE
,意思是当前进程不会被主动调度。
3.有一个判断条件condition
,这个判断条件表示的是当前进程是否能获得需要的资源。如果它为真,也就是当前进程能获得资源,就将当前进程对应的队列成员删除,退出函数。
4.如果条件不满足,那么任务调度(代码中体现就是运行schedule()
函数),并且除非condition
满足,当前进程不会被调度。
5.当资源准备好,相当于condition
变为真了,资源会查询到自己对应的队列头,然后通知睡眠在这个队列上的所有进程,这些进程会重新进行第3步,判断condition
,如果仍然为真,就会调度这个进程,并且从队列上删除这个进程。
上面每一步都对应了一些函数接口,比如wait_event(wq, condition)
就是让当前进程插入wq
队列头,这里的condition
就是需要满足的条件,它代表队列头资源是不是可用。
再比如资源发现自己准备好了,驱动程序就会调用__wake_up
函数代表唤醒队列上的某个进程或者所有进程。