在 Linux 内核中打印等待队列的内容时出现问题

在 Linux 内核中打印等待队列的内容时出现问题

语境:考虑以下一组操作 {A, B, C, D, E}:

  • (A):在我的设备驱动程序的功能上,如果驱动程序的缓冲区为空,read()我会将调用线程添加到等待队列中。wqbuf

更具体地说,调用线程通过以下方式添加到队列中:

wait_event_interruptible(wq, strlen(buf) > 0)
  • (B):类似地,在驱动程序的功能上,如果传递的命令是并且驱动程序的标志是,ioctl()我将调用线程添加到同一队列中。wqioctlMY_IOCTL_Xis_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。

这是我在控制台中得到的结果(简化的输出):

  1. “等待队列:[pid_1,pid_2]” //wake_up_interruptible()在调用之前write()
  2. “等待队列:[]” //wake_up_interruptible()调用write()(我在期待 [pid_2]
  3. “等待队列:[pid_2]” // 调用wake_up_interruptible()之前ioctl(MY_IOCTL_Y)
  4. “等待队列:[]” // 调用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()

  • 在 print #2 处,ioctl()任务实际上处于状态 0,即runnable [1]
  • 在 print #2 之后和 print #3 之前的任何时刻,任务都处于状态 1,即stopped [1]

相关内容