[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_tid
→lock_release
→sema_up
→thread_yield
→schedule
→process_activate
→pml4_activate
순서로 호출되어 패닉이 발생했습니다. 이는 초기 스레드에 TID를 할당하는 과정에서 락 해제 후 스케줄링이 발생하여 페이지 테이블이 완전히 초기화되기 전에pml4_activate
가 호출되기 때문입니다[1](https://stackoverflow.com/questions/52472084/pintos-userprog-all-tests-fail-is-kernel-vaddr).두 번째 콜 스택에서는
palloc_init
→resolve_area_info
→printf
→vprintf
→release_console
→lock_release
→sema_up
→thread_yield
순으로 호출되어 같은 문제가 발생했습니다. 이는 초기화 과정의 다른 부분에서도 동일한 패턴으로 문제가 발생함을 보여줍니다[4](https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html).핵심 원인
- 커널 초기화 과정에서 페이지 테이블이 완전히 설정되기 전에 스레드 스케줄링이 발생합니다[3](https://casys-kaist.github.io/pintos-kaist/appendix/synchronization.html).
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).- 초기화 단계에서 여러 시스템 컴포넌트(콘솔, 메모리 할당자 등)가 락을 사용하며, 이들이 모두 같은 문제를 일으킬 수 있습니다[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).종합적인 해결책
스레딩 시작 플래그 도입:
c*// thread.c* bool threading_started = false; *// thread.h* extern bool threading_started; *// thread_start() 함수 끝에 추가* void thread_start(void) { *// 기존 코드...* threading_started = true; }
모든 스레드 스케줄링 함수 수정:
cvoid thread_yield(void) { *// 스레딩이 시작되지 않았거나 인터럽트 컨텍스트면 리턴* if (!threading_started || intr_context()) return; *// 기존 코드...* } void thread_block(void) { *// 스레딩이 시작되지 않았으면 리턴* if (!threading_started) return; *// 기존 코드...* }
인터럽트 컨텍스트 확인 추가:
여러 소스에서 언급된 것처럼[7](https://terryahn.tistory.com/27)[[8](https://insengnewbie.tistory.com/353)](https://insengnewbie.tistory.com/353), 인터럽트 컨텍스트에서
thread_yield
가 호출되는 것도 문제입니다. 이 조건도 추가해야 합니다.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(); } }
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() 호출 제거* }
추가 고려사항
인터럽트 컨텍스트 확인 중요성:
intr_context()
확인은 매우 중요합니다. 인터럽트 핸들러 내에서thread_yield()
가 호출되면 커널 패닉이 발생합니다[7](https://terryahn.tistory.com/27)[[8](https://insengnewbie.tistory.com/353)](https://insengnewbie.tistory.com/353).세마포어와 락 사용 시점:
초기화 과정에서 세마포어와 락 사용을 최소화하고, 필요한 경우 스레딩 시작 여부를 확인해야 합니다[3](https://casys-kaist.github.io/pintos-kaist/appendix/synchronization.html).
디버깅 방법:
문제 해결 과정에서
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:
- https://stackoverflow.com/questions/52472084/pintos-userprog-all-tests-fail-is-kernel-vaddr
- http://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html
- https://casys-kaist.github.io/pintos-kaist/appendix/synchronization.html
- https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html
- https://cs162.org/static/proj/pintos-docs/docs/overview/debugging-panics/
- 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://terryahn.tistory.com/27
- https://insengnewbie.tistory.com/353
- https://velog.io/@bdbest72/pintOS-project1-threads-KAIST.ver
- https://askubuntu.com/questions/1427927/kernel-panic-gnu-grub-super-slow-acpi-bios-error
- https://cs162.org/static/proj/pintos-docs/docs/synch/semaphores/
- https://witch-work.pages.dev/pintos-4/
- https://web.stanford.edu/class/cs140/projects/pintos/pintos_10.html
- https://velog.io/@jhw607/PintOS2-User-Programs-Flow-of-PintOS
- https://github.com/casys-kaist/pintos-kaist/blob/master/userprog/process.c
- https://web.stanford.edu/~ouster/cgi-bin/cs140-spring20/pintos/pintos_6.html
- https://forum.proxmox.com/threads/solved-kernel-panic-on-install-rocky-linux.119841/
- https://web.stanford.edu/class/cs140/projects/pintos/pintos.pdf
- https://cs162.org/static/proj/pintos-docs/docs/development/general-tips/
- https://github.com/Dongdongshe/pintos-project-1/blob/master/README.md
- https://firecatlibrary.tistory.com/59
- https://www.andrew.cmu.edu/course/14-712-s20/pintos/pintos_2.html
- https://www.cs.usfca.edu/~benson/cs326-2005F/pintos/pintos.pdf
- http://web.stanford.edu/class/cs140/projects/pintos/pintos_2.html
- 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. 핵심 수정 사항
초기화 순서 조정:
cthreading_started = true; *// 추가/* Start thread scheduler and enable interrupts. */* thread_start ();
threading_started
플래그를thread_start()
호출 전에 설정함으로써, 스레드 스케줄러가 시작되기 전에 이미 스레딩이 시작된 것으로 표시합니다. 이는 초기화 과정에서 발생할 수 있는 불필요한 스케줄링을 방지합니다.세마포어 업데이트 수정:
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).~~
추가 동기화 함수 확인: → 보류(초기화 과정만 잘 넘겼다면 굳이 만질 필요 없을 거 같음)
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).인터럽트 컨텍스트 확인 일관성:(이 역시 마찬가지)
모든 스레드 스케줄링 관련 함수(
thread_yield()
,thread_block()
등)에서 인터럽트 컨텍스트 확인을 일관되게 적용하는 것이 좋습니다[1](https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html).문서화:
코드 변경 사항에 대한 주석을 추가하여 왜 이러한 검사가 필요한지 설명하는 것이 좋습니다. 이는 향후 코드를 유지 관리하는 데 도움이 됩니다.
결론
구현한 해결책은 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:
- https://web.stanford.edu/class/cs140/projects/pintos/pintos_6.html
- https://velog.io/@bdbest72/pintOS-project1-threads-KAIST.ver
- http://web.stanford.edu/class/cs140/projects/pintos/pintos_2.html
- https://oslab.kaist.ac.kr/ee415-spring-2022-proj1/
- https://github.com/Mohit17067/Priority-Scheduling-Pintos/blob/master/src/threads/thread.c
- https://jeason.gitbooks.io/pintos-reference-guide-sysu/Threads.html
- https://courses.cs.vt.edu/cs4284/spring2025/pintosdocs/pintos_2.html
- https://velog.io/@shinhojung814/WEEK-08-PintOS-Project-1-Threads-Advanced-Scheduler
- https://casys-kaist.github.io/pintos-kaist/project1/introduction.html
- https://grail.eecs.csuohio.edu/~cis345s/PintosCSU_Ref.pdf
Perplexity로부터의 답변: [pplx.ai/share](https://www.perplexity.ai/search/pplx.ai/share)