[ISSUE TRACKING] - Week9-11-team03/Pintos-User-Program GitHub Wiki

이슈 해결 과정을 기록합니다.

Issue2: 커널 초기화 문제 (가설 추적 방식) - 실패

Phase 0. 증상

pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/args-none:args-none -- -q   -f run args-none < /dev/null 2> tests/userprog/args-none.errors > tests/userprog/args-none.output
perl -I../.. ../../tests/userprog/args-none.ck tests/userprog/args-none tests/userprog/args-none.result
FAIL tests/userprog/args-none
Kernel panic in run: PANIC at ../../threads/mmu.c:206 in pml4_activate(): assertion `is_kernel_vaddr(pml4 ? pml4 : base_pml4)' failed.
Call stack: 0x8004217f23 0x800420ce73 0x800421b90d 0x8004207a2d 0x80042078df 0x800420726e 0x8004209fee 0x800420a5b6 0x8004207b37 0x8004206b06 0x8004206068
Translation of call stack:
0x0000008004217f23: debug_panic (lib/kernel/debug.c:32)
0x000000800420ce73: pml4_activate (threads/mmu.c:206)
0x000000800421b90d: process_activate (userprog/process.c:257)
0x0000008004207a2d: schedule (threads/thread.c:610)
0x00000080042078df: do_schedule (threads/thread.c:588)
0x000000800420726e: thread_yield (threads/thread.c:340)
0x0000008004209fee: sema_up (threads/synch.c:122)
0x000000800420a5b6: lock_release (threads/synch.c:263)
0x0000008004207b37: allocate_tid (threads/thread.c:642)
0x0000008004206b06: thread_init (threads/thread.c:121)
0x0000008004206068: main (threads/init.c:83)
pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/args-single:args-single -- -q   -f run 'args-single onearg' < /dev/null 2> tests/userprog/args-single.errors > tests/userprog/args-single.output
perl -I../.. ../../tests/userprog/args-single.ck tests/userprog/args-single tests/userprog/args-single.result
FAIL tests/userprog/args-single
Kernel panic in run: PANIC at ../../threads/mmu.c:206 in pml4_activate(): assertion `is_kernel_vaddr(pml4 ? pml4 : base_pml4)' failed.
Call stack: 0x8004217f23 0x800420ce73 0x800421b90d 0x8004207a2d 0x80042078df 0x800420726e 0x8004209fee 0x800420a5b6 0x8004207b37 0x8004206b06 0x8004206068
Translation of call stack:
0x0000008004217f23: debug_panic (lib/kernel/debug.c:32)
0x000000800420ce73: pml4_activate (threads/mmu.c:206)
0x000000800421b90d: process_activate (userprog/process.c:257)
0x0000008004207a2d: schedule (threads/thread.c:610)
0x00000080042078df: do_schedule (threads/thread.c:588)
0x000000800420726e: thread_yield (threads/thread.c:340)
0x0000008004209fee: sema_up (threads/synch.c:122)
0x000000800420a5b6: lock_release (threads/synch.c:263)
0x0000008004207b37: allocate_tid (threads/thread.c:642)
0x0000008004206b06: thread_init (threads/thread.c:121)
0x0000008004206068: main (threads/init.c:83)

초기에 initial thread에게 아이디를 할당하는 과정에 세마포를 건드리면서 yield가 실행되며 문제가 터짐.

Phase 1. 가설

초기 실행 과정에는 tid를 락 획득 없이 건들 수 있도록 수정하면 allocated_tid 이후 문제가 생기지 않을 것.

Phase 2. 불변식

allocate_tid는 lock_release로 이어진다.

Phase 3. OLF


// thread.c
bool threading_started = false;

// thread.h
extern bool threading_started;

// at the end of thread_start()
threading_started = true;

/* Returns a tid to use for a new thread. */
static tid_t
allocate_tid(void)
{
	static tid_t next_tid = 1;
	tid_t tid;

	if (threading_started){
		lock_acquire(&tid_lock);
		tid = next_tid++;
		lock_release(&tid_lock);
	}
	else {
		tid = next_tid++;
	}
	return tid;
}

Phase 4. 불변식 침해 확인

pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/args-none:args-none -- -q   -f run args-none < /dev/null 2> tests/userprog/args-none.errors > tests/userprog/args-none.output
perl -I../.. ../../tests/userprog/args-none.ck tests/userprog/args-none tests/userprog/args-none.result
FAIL tests/userprog/args-none
Kernel panic in run: PANIC at ../../threads/mmu.c:206 in pml4_activate(): assertion `is_kernel_vaddr(pml4 ? pml4 : base_pml4)' failed.
Call stack: 0x8004217f61 0x800420ceb1 0x800421b94b 0x8004207a3a 0x80042078ec 0x800420727b 0x800420a02c 0x800420a5f4 0x800421b225 0x800421b2d2 0x8004215464 0x800420ae17 0x800420b53e 0x8004206080
Translation of call stack:
0x0000008004217f61: debug_panic (lib/kernel/debug.c:32)
0x000000800420ceb1: pml4_activate (threads/mmu.c:206)
0x000000800421b94b: process_activate (userprog/process.c:257)
0x0000008004207a3a: schedule (threads/thread.c:612)
0x00000080042078ec: do_schedule (threads/thread.c:590)
0x000000800420727b: thread_yield (threads/thread.c:342)
0x000000800420a02c: sema_up (threads/synch.c:122)
0x000000800420a5f4: lock_release (threads/synch.c:263)
0x000000800421b225: release_console (lib/kernel/console.c:103)
0x000000800421b2d2: vprintf (lib/kernel/console.c:125)
0x0000008004215464: printf (lib/stdio.c:80)
0x000000800420ae17: resolve_area_info (threads/palloc.c:93)
0x000000800420b53e: palloc_init (threads/palloc.c:247)
0x0000008004206080: main (threads/init.c:86)

불변식은 바뀌긴 했지만 threading_started가 true로 바뀌기 전에 세마포를 쓰는 함수가 많고, 그것들이 전부 문제를 일으키고 있다.

Issue2에 대한 브리핑

Pintos 커널 초기화 문제 분석 및 해결 방안

초기 커널 초기화 과정에서 발생하는 패닉 문제를 분석해보겠습니다. 콜 스택을 통해 문제의 근본 원인과 해결 방법을 찾아보겠습니다.

문제 분석

처음 콜 스택을 보면 allocate_tidlock_releasesema_upthread_yieldscheduleprocess_activatepml4_activate 순서로 호출되어 패닉이 발생했습니다. 이는 초기 스레드에 TID를 할당하는 과정에서 락 해제 후 스케줄링이 발생하여 페이지 테이블이 완전히 초기화되기 전에 pml4_activate가 호출되기 때문입니다[1](https://stackoverflow.com/questions/52472084/pintos-userprog-all-tests-fail-is-kernel-vaddr).

두 번째 콜 스택에서는 palloc_initresolve_area_infoprintfvprintfrelease_consolelock_releasesema_upthread_yield 순으로 호출되어 같은 문제가 발생했습니다. 이는 초기화 과정의 다른 부분에서도 동일한 패턴으로 문제가 발생함을 보여줍니다[4](https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html).

핵심 원인

  1. 커널 초기화 과정에서 페이지 테이블이 완전히 설정되기 전에 스레드 스케줄링이 발생합니다[3](https://casys-kaist.github.io/pintos-kaist/appendix/synchronization.html).
  2. lock_release 함수가 sema_up을 호출하고, 이는 다시 thread_yield를 호출합니다[3](https://casys-kaist.github.io/pintos-kaist/appendix/synchronization.html)[[8](https://insengnewbie.tistory.com/353)](https://insengnewbie.tistory.com/353).
  3. 초기화 단계에서 여러 시스템 컴포넌트(콘솔, 메모리 할당자 등)가 락을 사용하며, 이들이 모두 같은 문제를 일으킬 수 있습니다[4](https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html).

해결 방안

Phase 3에서 시도한 방법은 좋은 접근이지만, 불완전합니다. 단순히 allocate_tid만 수정하는 것이 아니라, 스레드 스케줄링 자체를 초기화 과정에서 방지해야 합니다[1](https://stackoverflow.com/questions/52472084/pintos-userprog-all-tests-fail-is-kernel-vaddr)[[3](https://casys-kaist.github.io/pintos-kaist/appendix/synchronization.html)](https://casys-kaist.github.io/pintos-kaist/appendix/synchronization.html).

종합적인 해결책

  1. 스레딩 시작 플래그 도입:

    c*// thread.c*
    bool threading_started = false;
    
    *// thread.h*
    extern bool threading_started;
    
    *// thread_start() 함수 끝에 추가*
    void thread_start(void) {
      *// 기존 코드...*
      threading_started = true;
    }
    
  2. 모든 스레드 스케줄링 함수 수정:

    cvoid thread_yield(void) {
      *// 스레딩이 시작되지 않았거나 인터럽트 컨텍스트면 리턴*
      if (!threading_started || intr_context())
        return;
      
      *// 기존 코드...*
    }
    
    void thread_block(void) {
      *// 스레딩이 시작되지 않았으면 리턴*
      if (!threading_started)
        return;
      
      *// 기존 코드...*
    }
    
  3. 인터럽트 컨텍스트 확인 추가:

    여러 소스에서 언급된 것처럼[7](https://terryahn.tistory.com/27)[[8](https://insengnewbie.tistory.com/353)](https://insengnewbie.tistory.com/353), 인터럽트 컨텍스트에서 thread_yield가 호출되는 것도 문제입니다. 이 조건도 추가해야 합니다.

  4. test_max_priority 함수 수정 (우선순위 스케줄링 사용 시):

    cvoid test_max_priority(void) {
      if (!threading_started || intr_context() || list_empty(&ready_list))
        return;
      
      struct thread *curr = thread_current();
      struct list_elem *highest_elem = list_begin(&ready_list);
      
      if (less_priority(&curr->elem, highest_elem, NULL)) {
        thread_yield();
      }
    }
    
  5. lock_release 함수 수정:

    cvoid lock_release(struct lock *lock) {
      ASSERT(lock != NULL);
      ASSERT(lock_held_by_current_thread(lock));
      
      lock->holder = NULL;
      sema_up(&lock->semaphore);
      
      *// thread_yield() 호출 제거*
    }
    

추가 고려사항

  1. 인터럽트 컨텍스트 확인 중요성:

    intr_context() 확인은 매우 중요합니다. 인터럽트 핸들러 내에서 thread_yield()가 호출되면 커널 패닉이 발생합니다[7](https://terryahn.tistory.com/27)[[8](https://insengnewbie.tistory.com/353)](https://insengnewbie.tistory.com/353).

  2. 세마포어와 락 사용 시점:

    초기화 과정에서 세마포어와 락 사용을 최소화하고, 필요한 경우 스레딩 시작 여부를 확인해야 합니다[3](https://casys-kaist.github.io/pintos-kaist/appendix/synchronization.html).

  3. 디버깅 방법:

    문제 해결 과정에서 barrier() 함수를 사용하여 특정 지점에 브레이크포인트를 설정하고 GDB로 디버깅하는 것이 도움이 됩니다[5](https://cs162.org/static/proj/pintos-docs/docs/overview/debugging-panics/).

결론

Pintos의 커널 초기화 문제는 스레드 스케줄링이 너무 일찍 발생하는 것이 근본 원인입니다. 이를 해결하기 위해 스레딩 시작 플래그를 도입하고, 모든 스레드 스케줄링 함수에서 이 플래그와 인터럽트 컨텍스트를 확인하는 방식으로 접근해야 합니다.

이 문제는 Pintos 프로젝트에서 자주 발생하는 문제로, 운영체제의 초기화 순서와 동기화 메커니즘의 중요성을 보여줍니다[1](https://stackoverflow.com/questions/52472084/pintos-userprog-all-tests-fail-is-kernel-vaddr)[[6](https://velog.io/@sjlim32/%ED%81%AC%EB%9E%98%ED%94%84%ED%86%A4-%EC%A0%95%EA%B8%80-Week8-Pintos-Project-2-%ED%9A%8C%EA%B3%A0)](https://velog.io/@sjlim32/%ED%81%AC%EB%9E%98%ED%94%84%ED%86%A4-%EC%A0%95%EA%B8%80-Week8-Pintos-Project-2-%ED%9A%8C%EA%B3%A0).

Citations:

  1. https://stackoverflow.com/questions/52472084/pintos-userprog-all-tests-fail-is-kernel-vaddr
  2. http://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html
  3. https://casys-kaist.github.io/pintos-kaist/appendix/synchronization.html
  4. https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html
  5. https://cs162.org/static/proj/pintos-docs/docs/overview/debugging-panics/
  6. https://velog.io/@sjlim32/%ED%81%AC%EB%9E%98%ED%94%84%ED%86%A4-%EC%A0%95%EA%B8%80-Week8-Pintos-Project-2-%ED%9A%8C%EA%B3%A0
  7. https://terryahn.tistory.com/27
  8. https://insengnewbie.tistory.com/353
  9. https://velog.io/@bdbest72/pintOS-project1-threads-KAIST.ver
  10. https://askubuntu.com/questions/1427927/kernel-panic-gnu-grub-super-slow-acpi-bios-error
  11. https://cs162.org/static/proj/pintos-docs/docs/synch/semaphores/
  12. https://witch-work.pages.dev/pintos-4/
  13. https://web.stanford.edu/class/cs140/projects/pintos/pintos_10.html
  14. https://velog.io/@jhw607/PintOS2-User-Programs-Flow-of-PintOS
  15. https://github.com/casys-kaist/pintos-kaist/blob/master/userprog/process.c
  16. https://web.stanford.edu/~ouster/cgi-bin/cs140-spring20/pintos/pintos_6.html
  17. https://forum.proxmox.com/threads/solved-kernel-panic-on-install-rocky-linux.119841/
  18. https://web.stanford.edu/class/cs140/projects/pintos/pintos.pdf
  19. https://cs162.org/static/proj/pintos-docs/docs/development/general-tips/
  20. https://github.com/Dongdongshe/pintos-project-1/blob/master/README.md
  21. https://firecatlibrary.tistory.com/59
  22. https://www.andrew.cmu.edu/course/14-712-s20/pintos/pintos_2.html
  23. https://www.cs.usfca.edu/~benson/cs326-2005F/pintos/pintos.pdf
  24. http://web.stanford.edu/class/cs140/projects/pintos/pintos_2.html
  25. https://pkuflyingpig.gitbook.io/pintos/appendix/reference-guide/synchronization

Perplexity로부터의 답변: [pplx.ai/share](https://www.perplexity.ai/search/pplx.ai/share)

Issue3: Issue2에 대한 후속 조치

Phase 1. 수정 내용

init.c

int
main (void) {
	uint64_t mem_end;
	char **argv;

	/* Clear BSS and get machine's RAM size. */
	bss_init ();

	/* Break command line into arguments and parse options. */
	argv = read_command_line ();
	argv = parse_options (argv);

#ifdef USERPROG
	exception_init ();
	syscall_init ();
#endif
	threading_started = true; // 추가
	/* Start thread scheduler and enable interrupts. */
	thread_start ();
	serial_init_queue ();
	timer_calibrate ();

synch.c


void sema_up(struct semaphore *sema)
{
	enum intr_level old_level;

	ASSERT(sema != NULL);

	old_level = intr_disable();
	if (!list_empty(&sema->waiters))
	{
		list_sort(&sema->waiters, cmp_priority, NULL);
		thread_unblock(list_entry(list_pop_front(&sema->waiters),
								  struct thread, elem));
	}
	sema->value++;
	intr_set_level(old_level);
	if (threading_started && !intr_context()) // 추가
	{
		thread_yield();
	}
}

위와 같이 부팅 과정에 yield 호출을 금지해서 해결. 부팅 도중의 커널 패닉 문제는 완전히 해결 된 것으로 보임.

아래는 예시로 볼 수 있는 결과

jungle@ff1c877267a2:/workspaces/Pintos-User-Program/pintos-kaist/userprog/build$ pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/args-none:args-none -- -q   -f run args-none
qemu-system-x86_64: warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
Kernel command line: -q -f put args-none run args-none
0 ~ 9fc00 1
100000 ~ 13e0000 1
Pintos booting with: 
        base_mem: 0x0 ~ 0x9fc00 (Usable: 639 kB)
        ext_mem: 0x100000 ~ 0x13e0000 (Usable: 19,328 kB)
Calibrating timer...  19,635,200 loops/s.
hd0:0: detected 313 sector (156 kB) disk, model "QEMU HARDDISK", serial "QM00001"
hd0:1: detected 20,160 sector (9 MB) disk, model "QEMU HARDDISK", serial "QM00002"
hd1:0: detected 102 sector (51 kB) disk, model "QEMU HARDDISK", serial "QM00003"
Formatting file system...done.
Boot complete.
Putting 'args-none' into the file system...
Executing 'args-none':
Execution of 'args-none' complete.
Timer: 117 ticks
Thread: 41 idle ticks, 76 kernel ticks, 1 user ticks
hd0:0: 0 reads, 0 writes
hd0:1: 35 reads, 232 writes
hd1:0: 102 reads, 0 writes
Console: 787 characters output
Keyboard: 0 keys pressed
Exception: 0 page faults
Powering off...

Phase 2. 피드백

답변

Pintos 부팅 과정 동기화 문제 해결 분석

Phase 1에서 구현한 해결책은 Pintos 커널 초기화 과정에서 발생하는 스케줄링 문제를 효과적으로 해결한 것으로 보입니다. 이 접근 방식은 커널 초기화 과정에서 스레드 스케줄링을 방지하여 페이지 테이블이 완전히 설정되기 전에 pml4_activate()가 호출되는 문제를 방지합니다.

해결책 분석

1. 핵심 수정 사항

  1. 초기화 순서 조정:

    cthreading_started = true; *// 추가/* Start thread scheduler and enable interrupts. */* thread_start ();

    threading_started 플래그를 thread_start() 호출 전에 설정함으로써, 스레드 스케줄러가 시작되기 전에 이미 스레딩이 시작된 것으로 표시합니다. 이는 초기화 과정에서 발생할 수 있는 불필요한 스케줄링을 방지합니다.

  2. 세마포어 업데이트 수정:

    cif (threading_started && !intr_context()) *// 추가* { thread_yield(); }

    sema_up() 함수에서 thread_yield()를 호출하기 전에 스레딩이 시작되었는지와 인터럽트 컨텍스트가 아닌지 확인합니다. 이는 초기화 과정과 인터럽트 핸들러에서 스케줄링이 발생하는 것을 방지합니다.

2. 효과

실행 결과를 보면 args-none 테스트가 성공적으로 실행되었습니다:

  • 부팅 과정이 완료되었습니다 ("Boot complete.")
  • 파일 시스템에 args-none이 추가되었습니다
  • args-none의 실행이 완료되었습니다
  • 시스템이 정상적으로 종료되었습니다 ("Powering off...")

이는 이전에 발생했던 커널 패닉이 더 이상 발생하지 않음을 보여줍니다.

개선 제안

현재 해결책은 효과적이지만, 몇 가지 개선 사항을 고려할 수 있습니다:

1. ~~일관된 스레딩 시작 관리:~~

`~~threading_started` 변수를 설정하는 위치가 `thread_start()` 함수 외부에 있습니다. 이상적으로는 이 변수를 `thread_start()` 함수 내부에서 설정하여 스레드 시스템의 초기화와 플래그 설정을 함께 관리하는 것이 좋습니다[1](https://web.stanford.edu/class/cs140/projects/pintos/pintos_[6](https://jeason.gitbooks.io/pintos-reference-guide-sysu/Threads.html).html)[6](https://jeason.gitbooks.io/pintos-reference-guide-sysu/Threads.html).~~
  1. 추가 동기화 함수 확인: → 보류(초기화 과정만 잘 넘겼다면 굳이 만질 필요 없을 거 같음)

    sema_up() 외에도 lock_release(), cond_signal() 등 다른 동기화 함수들도 유사한 방식으로 수정이 필요할 수 있습니다[3](http://web.stanford.edu/class/cs140/projects/pintos/pintos_2.html)[[5](https://github.com/Mohit17067/Priority-Scheduling-Pintos/blob/master/src/threads/thread.c)](https://github.com/Mohit17067/Priority-Scheduling-Pintos/blob/master/src/threads/thread.c).

  2. 인터럽트 컨텍스트 확인 일관성:(이 역시 마찬가지)

    모든 스레드 스케줄링 관련 함수(thread_yield(), thread_block() 등)에서 인터럽트 컨텍스트 확인을 일관되게 적용하는 것이 좋습니다[1](https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html).

  3. 문서화:

    코드 변경 사항에 대한 주석을 추가하여 왜 이러한 검사가 필요한지 설명하는 것이 좋습니다. 이는 향후 코드를 유지 관리하는 데 도움이 됩니다.

결론

구현한 해결책은 Pintos의 초기화 과정에서 발생하는 스케줄링 문제를 효과적으로 해결했습니다. 이 접근 방식은 운영체제 초기화 과정에서 중요한 원칙을 보여줍니다: 시스템의 모든 구성 요소가 완전히 초기화되기 전에 복잡한 동작(이 경우 스레드 스케줄링)을 수행하지 않는 것입니다[1](https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html)[[10](https://grail.eecs.csuohio.edu/~cis345s/PintosCSU_Ref.pdf)](https://grail.eecs.csuohio.edu/~cis345s/PintosCSU_Ref.pdf).

이 해결책은 Pintos 프로젝트의 다음 단계(사용자 프로그램 실행, 가상 메모리 등)를 진행하는 데 있어 안정적인 기반을 제공할 것입니다.

Citations:

  1. https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html
  2. https://velog.io/@bdbest72/pintOS-project1-threads-KAIST.ver
  3. http://web.stanford.edu/class/cs140/projects/pintos/pintos_2.html
  4. https://oslab.kaist.ac.kr/ee415-spring-2022-proj1/
  5. https://github.com/Mohit17067/Priority-Scheduling-Pintos/blob/master/src/threads/thread.c
  6. https://jeason.gitbooks.io/pintos-reference-guide-sysu/Threads.html
  7. https://courses.cs.vt.edu/cs4284/spring2025/pintosdocs/pintos_2.html
  8. https://velog.io/@shinhojung814/WEEK-08-PintOS-Project-1-Threads-Advanced-Scheduler
  9. https://casys-kaist.github.io/pintos-kaist/project1/introduction.html
  10. https://grail.eecs.csuohio.edu/~cis345s/PintosCSU_Ref.pdf

Perplexity로부터의 답변: [pplx.ai/share](https://www.perplexity.ai/search/pplx.ai/share)