PintOS PJT2를 시작했다.
1주차의 thread에 이어 2주차에선 user program을 다룬다.
이제 프로그램은 메모리나 자원에 직접 접근하지 못하고 kernel 호출을 통해 자원을 활용해야 한다.
프로젝트 시작에 앞서 make check를 돌려보니 에러가 발생했다.
Kernel panic in run: PANIC at ../../threads/thread.c:332 in thread_yield(): assertion `!intr_context ()' failed. Call stack: 0x80042184b5 0x8004207341 0x8004207833 0x800420a6df 0x8004214a7c 0x80042094d7 0x80042098f5 0x8004207ec3 Translation of call stack: 0x00000080042184b5: debug_panic (lib/kernel/debug.c:32) 0x0000008004207341: thread_yield (threads/thread.c:334) 0x0000008004207833: thread_test_preemption (threads/thread.c:478) 0x000000800420a6df: sema_up (threads/synch.c:127) 0x0000008004214a7c: interrupt_handler (devices/disk.c:526) 0x00000080042094d7: intr_handler (threads/interrupt.c:352) 0x00000080042098f5: intr_entry (threads/intr-stubs.o:?) 0x0000008004207ec3: kernel_thread (threads/thread.c:645)
원인은 sema_up 호출 시 thread_yield도 호출되었기 때문이었다.
외부 인터럽트가 발생 중일 때 thread_yield가 실행되어 에러가 발생했다.
외부 인터럽트 실행 중 thread_yield 실행하면 아래와 같은 문제가 발생할 수 있다.
thread_yield()는 현재 실행 중인 스레드를 레디 큐에 추가하고 다른 스레드로 전환하는 기능입니다. 그러나 인터럽트 컨텍스트에서 호출되면, 원래 실행 중이던 스레드의 상태가 정확히 저장되지 않아 스케줄링 오류가 발생할 수 있습니다.
인터럽트 핸들러가 중간에 다른 스레드로 전환되면서 중요한 상태 정보(레지스터 값, 현재 실행 위치 등)를 잃어버려 이후의 실행 흐름이 잘못될 수 있습니다.
thread_yield()는 현재 스레드의 컨텍스트를 저장하고 다른 스레드로 전환하는 과정에서, 컨텍스트 스위칭을 위해 스레드의 스택과 레지스터 값을 조정해야 합니다. 인터럽트 컨텍스트에서는 일반 스레드와 다른 스택을 사용하기 때문에 이러한 과정에서 스택 오염이 발생하거나 스택 포인터가 손상될 수 있습니다.
인터럽트 처리 중 스케줄링이 전환되면, 인터럽트 처리 도중에 사용된 데이터나 레지스터 값이 깨지거나 유실될 수 있습니다. 이로 인해 인터럽트의 후속 처리가 제대로 이루어지지 않거나, 다음에 호출된 인터럽트 핸들러가 예상치 못한 상태에서 시작될 위험이 있습니다.
인터럽트 컨텍스트에서 수행되는 thread_yield()는 시스템이 정상적으로 우선순위 기반 스케줄링을 하지 못하게 할 수 있습니다. 특히 멀티스레드 환경에서 인터럽트와 관련된 리소스에 대한 잠금 문제 등이 발생하여 데드락이나 우선순위 역전이 발생할 가능성이 높아집니다.
/* 수정 전 코드 */
void
thread_test_preemption (void) {
if (!list_empty (&ready_list) &&
thread_current ()->priority <
list_entry (list_front (&ready_list), struct thread, elem)->priority)
thread_yield ();
// current thread 의 priority 가 ready list 가장 앞 thread 보다 낮으면, current thread yield 하고, ready list 맨 앞 thread running 으로
}
수정 코드
/* priority 구현 */
void thread_test_preemption(void) {
// enum intr_level old_level = intr_disable();
if (!list_empty(&ready_list)) {
struct thread *curr = thread_current();
struct thread *tmp = list_entry(list_front(&ready_list), struct thread, elem);
// 현재 스레드와 ready_list에 있는 스레드의 우선순위를 비교
if (curr->priority < tmp->priority) { // ready_list에 있는 스레드의 우선순위가 더 높으면 yield
thread_yield();
}
}
// intr_set_level(old_level);
}
