语境:考虑以下一组操作 {A, B, C, D, E}:
- (A):在我的设备驱动程序的功能上,如果驱动程序的缓冲区为空,
read()
我会将调用线程添加到等待队列中。wq
buf
更具体地说,调用线程通过以下方式添加到队列中:
wait_event_interruptible(wq, strlen(buf) > 0)
- (B):类似地,在驱动程序的功能上,如果传递的命令是并且驱动程序的标志是,
ioctl()
我将调用线程添加到同一队列中。wq
ioctl
MY_IOCTL_X
is_free == 0
同样,调用线程通过以下方式添加到等待队列:
wait_event_interruptible(wq, is_free != 0)
(C):在驱动程序的
write()
函数中,我将用户空间内容传递给buff
,并调用wake_up_interruptible(&wq)
,以便唤醒 中处于“睡眠”状态的线程read()
。(D):在驱动程序的
ioctl()
功能上,如果ioctl
命令是MY_IOCTL_Y
,我设置is_free = 1
并调用wake_up_interruptible(&wq)
,以唤醒由 置于“睡眠”状态的线程ioctl(MY_IOCTL_X)
。(E):我创建了一个
print_wait_queue()
函数来打印等待队列中线程的 PID。我之前就这么称呼过之后调用wake_up_interruptible()
操作 C 和 D。
打印功能的实现如下:
void print_wait_queue(struct wait_queue_head* wq)
{
struct list_head *i, *tmp;
pr_info("waiting queue: [");
list_for_each_safe(i, tmp, &(wq->head))
{
struct wait_queue_entry* wq_item = list_entry(i, struct wait_queue_entry, entry);
struct task_struct* task = (struct task_struct*) wq_item->private;
pr_info("%d,", task->pid);
}
pr_info("]\n");
}
问题:实际的排队和出队似乎按预期工作,这里没有问题。
然而,等待队列的打印却不是。
假设我按以下顺序执行上述操作:A -> B -> C -> D。
这是我在控制台中得到的结果(简化的输出):
- “等待队列:[pid_1,pid_2]” //
wake_up_interruptible()
在调用之前write()
- “等待队列:[]” //
wake_up_interruptible()
调用write()
(我在期待 [pid_2]) - “等待队列:[pid_2]” // 调用
wake_up_interruptible()
之前ioctl(MY_IOCTL_Y)
- “等待队列:[]” // 调用
wake_up_interruptible()
后ioctl(MY_IOCTL_Y)
如上所示,在 print #2 处,剩余线程的 PID - pid_2 - 没有显示在 PID 列表中。相反,我得到一个空列表。
wake_up_interruptible()
然而,正如预期的那样,pid_2 在调用ioctl(MY_IOCTL_Y)
print #3之前就出现在列表中,这表明它pid_2
实际上保留在 print #2 和 #3 之间的等待队列中。
问题:为什么我在上面的 print #2 处没有得到 [pid_2],但在 #3 处却得到了它?
我尝试用锁保护等待队列周期print_wait_queue()
,但它没有解决打印问题。
我还确认了我传递给的指针的地址print_wait_queue()
始终指向同一地址。
答案1
我的观察是预期的行为。
如上所述这里,在第 6.2.2 节中:
wake_up()
醒来所有在给定队列上等待的进程(...)。另一种形式 (wake_up_interruptible()
) 将自身限制为执行可中断睡眠的进程。
因此,在上面的 print #2 中,在调用 后wake_up_interruptible()
,两个任务的状态立即为runnable
,因此,这两个任务都从等待队列中删除。然而,该ioctl()
任务即将再次进入休眠状态,因为其条件尚未得到验证。
我通过查看gdb
每个之前和之后的pid_2 任务状态来确认这一点wake_up_interruptible()
: