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)

  • print_wait_queue()(E):待ち行列にあるスレッドの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)

上記のように、印刷#2では、残りのスレッド(pid_2)のPIDはPIDリストに表示されません。代わりに空のリストが表示されます。

wake_up_interruptible()ただし、予想どおり、pid_2はioctl(MY_IOCTL_Y)印刷#3が呼び出される前にリストに表示され、これはpid_2実際には印刷#2と#3の間のキューに残っていることを示します。

質問:上記の印刷#2では[pid_2]が得られませんが、#3では何が得られますか?

ロックで待ち行列を保護しようとしましたが、print_wait_queue()印刷の問題は解決されませんでした。

また、私が渡したポインタのアドレスはprint_wait_queue()常に同じアドレスを指していることを確認しました。

答え1

私の観察は予想される動作です。

上記のようにここ、セクション6.2.2から:

wake_up()目覚める指定されたキューで待機しているすべてのプロセス(...).他の形式(wake_up_interruptible())は中断可能なスリープモードを実行するプロセスに制限されています。

したがって、上記の印刷#2では、wake_up_interruptible()呼び出しの直後に両方のジョブの状態があるrunnableため、両方のジョブは待機キューから削除されます。しかし、ioctl()まだ条件が検証されておらず、任務は再び休眠状態になる予定だ。

gdb各ジョブの前後のpid_2ジョブの状態を確認してこれを確認しましたwake_up_interruptible()

  • 印刷#2では、ジョブioctl()は実際には状態0にある。runnable [1]
  • 印刷#2の後と印刷#3の前にいつでも、ジョブは状態1にあります。stopped [1]

関連情報