在 Linux 核心中列印等待隊列的內容時出現問題
**上下文:**考慮以下一組操作 {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)
,以便喚醒 ‘sleep’ 中的執行緒read()
。- (D):在驅動
ioctl()
函式上,如果ioctl
命令是MY_IOCTL_Y
,我設置is_free = 1
,並呼叫wake_up_interruptible(&wq)
,以喚醒被 ‘sleep’ 設置的執行緒ioctl(MY_IOCTL_X)
。- (E):我創建了一個
print_wait_queue()
函式來列印等待隊列中執行緒的 PID。我在呼叫操作 C 和 D之前和之後呼叫它。wake_up_interruptible()
列印功能是這樣實現的:
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)
如上所示,在列印 #2 處,剩餘執行緒的 PID - pid_2 - 沒有出現在 PID 列表中。相反,我得到一個空列表。
但是,pid_2 在列印 #3 呼叫之前出現在列表中
wake_up_interruptible()
,ioctl(MY_IOCTL_Y)
正如預期的那樣,表明它pid_2
實際上保留在列印 #2 和 #3 之間的等待隊列中。**問題:**為什麼我沒有得到
$$ pid_2 $$在上面的列印 #2,然後在 #3 得到它? 我試過
print_wait_queue()
用鎖保護等待隊列週期,但沒有解決列印問題。我還確認了我傳遞的指針的地址
print_wait_queue()
總是指向同一個地址。
我的觀察是預期的行為。
如此處所述,在第 6.2.2 節中:
wake_up()
喚醒所有等待給定隊列(…)的程序。另一種形式 (wake_up_interruptible()
) 將自身限制為執行可中斷睡眠的程序。因此,在上面的列印 #2 處,在呼叫 之後
wake_up_interruptible()
,兩個任務的狀態都是runnable
,因此,兩個任務都從等待隊列中刪除。然而,ioctl()
任務即將再次進入休眠狀態,因為它的狀態尚未得到驗證。我通過查看 pid_2
gdb
之前和之後的任務狀態確認了這一點wake_up_interruptible()
: