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명령이 이면 "sleep" 상태에 있던 스레드를 깨우도록 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].

관련 정보