Project 02: xv6 RISC-V Kernel-Level Threads Implementation - wiki
Design
Project Overview
๋ณธ project์ ํต์ฌ ๋ชฉํ๋ xv6 RISC-V ๊ธฐ๋ฐ ์ด์์ฒด์ ์ kernel-level thread (์ปค๋ ์์ค thread) ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒ์ด๋ค. xv6๋ MIT์์ ๊ต์ก ๋ชฉ์ ์ผ๋ก ์ค๊ณํ ๊ฐ๊ฒฐํ UNIX ๊ธฐ๋ฐ OS๋ก, ๋ค์ค ํ๋ก์ธ์ค(multiprocessing)๋ ์ง์ํ์ง๋ง ๋ค์ค ์ค๋ ๋(multithreading)๋ ์ง์ํ์ง ์๋๋ค. ๋ณธ project๋ ๊ธฐ์กด xv6์ process ๊ด๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์ต๋ํ ํ์ฉํ๋ฉด์, thread์ ์คํ ์ปจํ ์คํธ(context), ์์ ๊ณต์ ๋ฐฉ์, ๋๊ธฐํ ์๋๋ฆฌ์ค ๋ฑ์ ๊ตฌํํ๋ ๋ฐ ์ค์ ์ ๋๋ค.
Thread๋ process์ ๋ฌ๋ฆฌ ์ฃผ์ ๊ณต๊ฐ์ ๊ณต์ ํ๋ฉฐ, ์ฌ์ฉ์ ์์ค stack๊ณผ register๋ง ๋
๋ฆฝ์ ์ด๋ค. ๋ฐ๋ผ์ xv6์ proc ๊ตฌ์กฐ์ฒด๋ฅผ ๊ทธ๋๋ก ์ฌํ์ฉํ๋, thread ์ฌ๋ถ๋ฅผ ๋ํ๋ด๋ flag is_thread๋ฅผ ์๋กญ๊ฒ ์ถ๊ฐํ๊ณ , ๊ทธ์ ๋ฐ๋ผ clone(), join(), exec(), kill(), sleep(), wakeup() ๋ฑ์ ๋์์ ์์ ํ์๋ค.
Design Principles and Requirements Strategy
1. Address Space Sharing (pagetable)
Thread์ ๋ณธ์ง์ ํ๋์ ์ฃผ์ ๊ณต๊ฐ์ ๊ณต์ ํ๋ ๋ค์ค ์คํ ๊ฒฝ๋ก์ด๋ค. ๋ฐ๋ผ์ clone()์ผ๋ก ์ thread๋ฅผ ์์ฑํ ๋์๋ fork()์ฒ๋ผ ์๋ก์ด pagetable์ ๋ณต์ฌํด์๋ ์ ๋๊ณ , ๊ธฐ์กด process์ pagetable์ ๊ณต์ ํด์ผ ํ๋ค. ๋จ, trapframe๊ณผ context๋ thread๋ง๋ค ๊ฐ๋ณ์ ์ผ๋ก ๊ฐ์ ธ์ผ ํ๋ฏ๋ก allocproc()์ ํตํด ์๋ก์ด proc์ ํ ๋นํ๋ค.
๋ณ๊ฒฝ ์ฌํญ
uvmshare()ํจ์๋ฅผ ์ง์ ๊ตฌํํ์ฌ, ๋ถ๋ชจ์ user PTE๋ฅผ ์์ pagetable์ ์ง์ ๋งคํํจ.clone()์์np->pagetable = proc_pagetable(np);ํuvmshare(np->pagetable, p->pagetable, p->sz);ํธ์ถ
2. User Stack Separation
๊ฐ thread๋ ๋
๋ฆฝ๋ ์ฌ์ฉ์ stack์ ํ์๋ก ํ๋ค. ๋ฐ๋ผ์ ์ฌ์ฉ์ ์์ญ์์ thread_create()๊ฐ ํธ์ถ๋๋ฉด, sbrk(PGSIZE)๋ก 1page ๋จ์์ stack memory๋ฅผ ํ ๋นํ๊ณ , ํด๋น ์ฃผ์๋ฅผ clone()์ ์ธ์๋ก ์ ๋ฌํด์ผ ํ๋ค. kernel์์๋ trapframe->sp๋ฅผ ํด๋น ์ฃผ์์ top(+PGSIZE)๋ก ์ค์ ํ์ฌ ์คํ์ ๊ฐ์ํ๋ค.
๋ณ๊ฒฝ ์ฌํญ
thread_create()์์ page-aligned stack์ ํ๋ณด (s = sbrk(PGSIZE)), ์คํจ ์ -1 ๋ฐํclone()๋ด์์ stack ์ ํจ์ฑ ๊ฒ์ฆ: null ์ฒดํฌ, page-alignment ํ์ธ,walkaddr()๋ก ๋ฌผ๋ฆฌ ์ฃผ์ ๊ฒ์ฆ
3. Function Pointer-Based Thread Entry Point
xv6์์๋ ์ ์ ํจ์ ํฌ์ธํฐ๋ฅผ ์ง์ trapframe์ ์ค์ ํ์ฌ ์ง์
์ ์ ์ ๊ณตํด์ผ ํ๋ค. ๋ฐ๋ผ์ clone()์์ np->trapframe->epc = (uint64)fcn;์ผ๋ก fcn์ ์ค์ ํ๊ณ , arg1๊ณผ arg2๋ ๊ฐ๊ฐ a0, a1 register์ ์ธํ
ํ๋ค. ์ด๋ก์จ ์๋ก ์์ฑ๋ thread๋ usertrapret() ์ดํ ํด๋น ํจ์๋ก jumpํ๊ฒ ๋๋ค.
ํน์ด์
- ๋ณธ ๊ณผ์ ์์๋
fcn == 0x0์ธ ๊ฒฝ์ฐ๊ฐ ์ ์์ ์ด๋ฉฐ, ๊ณผ์ ๋ช ์ธ์์ ์ ๊ณตํ ํ ์คํธ๊ฐ ์ด๋ฅผ ๊ฐ์ ์ ์ผ๋ก ์ค์ ํด๋์์ โ ๋ฌดํจ ์ฃผ์๊ฐ ์๋ - ๋ฐ๋ผ์
fcn == 0์ผ ๊ฒฝ์ฐ ์์ธ ์ฒ๋ฆฌํ์ง ์๋๋ก ํจ
4. Thread Identification (is_thread)
process์ thread๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด proc ๊ตฌ์กฐ์ฒด์ is_thread flag๋ฅผ ์ถ๊ฐํ์๋ค. ์ด๋ฅผ ํตํด join(), exec(), kill() ๋ฑ์์ ์กฐ๊ฑด ๋ถ๊ธฐ๋ฅผ ์ํํ ์ ์๋ค.
์ ์ฉ ์์น
clone()์์๋np->is_thread = 1;fork()์์๋np->is_thread = 0;๋ก ๋ช ์์ ์ผ๋ก ์ด๊ธฐํ
5. Stack Address Reclamation on Exit
thread๊ฐ ์ข
๋ฃ๋๋ฉด, ๊ทธ thread๊ฐ ์ฌ์ฉํ๋ stack์ ๋ ์ด์ ์ฌ์ฉ๋์ง ์์ผ๋ฏ๋ก, join() ์ ํด๋น ์ฃผ์๋ฅผ user์๊ฒ ๋๊ฒจ sbrk(-PGSIZE)๋ก memory๋ฅผ ๋ฐ๋ฉํด์ผ ํ๋ค.
๊ตฌํ ๋ฐฉ์
clone()์np->ustack = (uint64)stack;์ผ๋ก ์ ์ฅjoin()์copyout()์ ํตํด user stack ์ฃผ์ ์ ๋ฌthread_join()์์sbrk(-PGSIZE)๋ก ํ์
6. Remove All Threads on exec()
exec()๋ ํด๋น process์ pagetable์ ์์ ํ ๋ฎ์ด์์ฐ๋ ์์
์ด๋ค. ๋ฐ๋ผ์ ๊ฐ์ pagetable์ ๊ณต์ ํ๋ ๋ค๋ฅธ thread๋ค๋ ๋ชจ๋ ์ ๊ฑฐํ์ง ์์ผ๋ฉด memory corruption์ด ๋ฐ์ํ๋ค.
ํด๊ฒฐ ๋ฐฉ์
exec()์ง์ ์,proc[NPROC]์ ์ํํ๋ฉฐpagetable == p->pagetable && p != self์ธ ๋ชจ๋ proc์ ๋ํดfreeproc()ํธ์ถ- ์ดํ ์๋ก์ด pagetable์
proc_pagetable()๋ก ์ฌ์์ฑํ๊ณ , ELF ๋ฐ์ด๋๋ฆฌ ๋ก๋ ์ํ
7. Terminate All Threads on kill()
RISC-V์์๋ process๋ฅผ ์ฃฝ์ด๊ธฐ ์ํด์ ๊ทธ ์ฃผ์ ๊ณต๊ฐ์ ๊ณต์ ํ๋ ๋ชจ๋ thread๋ฅผ ์ข
๋ฃ์์ผ์ผ ํ๋ค. ๋ฐ๋ผ์ kill(pid)๋ ๋จ์ผ pid๋ฅผ ๋์์ผ๋ก ํ๋, ๋ด๋ถ์ ์ผ๋ก๋ ํด๋น pagetable์ ๊ณต์ ํ๋ ๋ชจ๋ proc์ ์ข
๋ฃ ์ฒ๋ฆฌํด์ผ ํ๋ค.
๋ณ๊ฒฝ ์ฌํญ
kill()๋ด๋ถ์์target->pagetable์ ๊ธฐ์ค์ผ๋ก, ๊ฐ์ pagetable์ ๊ณต์ ํ๋ ๋ชจ๋ proc์ ๋ํดp->killed = 1์ค์ SLEEPING์ํ์ธ thread๋RUNNABLE๋ก ๋ณ๊ฒฝํ์ฌsched()์ ์ง์ ๊ฐ๋ฅํ๊ฒ ํจ
8. Separate wait/join
- ๊ธฐ์กด์
wait()๋ process๋ง ์๊ฑฐํด์ผ ํ๋ค. - thread๋
join()์ ํตํด ์๊ฑฐํ๋ฏ๋กwait()์๋ ๋ ๋ฆฝ์ ์ผ๋ก ๊ตฌํ๋๋ค. join()์proc[NPROC]์ ์ํํ๋ฉฐparent == myproc()์ด๊ณis_thread == 1์ด๋ฉฐZOMBIE์ํ์ธ thread๋ฅผ ์ฐพ์ ํ์ํ๋ค.
9. Guarantee sleep/wakeup Semantics
thread๊ฐ sleep() ์ํ์ ๋ค์ด๊ฐ ๋, p->lock์ ๋ฐ๋์ ๋จผ์ ํ๋ํ ํ sched()๋ก ๋๊ฒจ์ผ wakeup()์์์ ๋๊ธฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๋๋ค. ์ด ๊ตฌ์กฐ๋ ๊ธฐ์กด xv6์์ ๊ทธ๋๋ก ์ ์ง๋๋ฉฐ, thread ํ๊ฒฝ์์๋ ๋ฌธ์ ๊ฐ ์๋ค.
Design Key Summary
| Element | Implementation Strategy Summary |
|---|---|
| Address Space | uvmshare()๋ก parent์ pagetable ๊ณต์ |
| User Stack | sbrk(PGSIZE)๋ก ํ๋ณด, clone()์ ์ ๋ฌํ์ฌ trapframe->sp ์ค์ |
| Function Pointer Entry | trapframe->epc = fcn |
| Argument Passing | a0 = arg1, a1 = arg2 |
| TCB Separation | allocproc() โ ๋
๋ฆฝ๋ trapframe/context |
| Thread Identification | is_thread == 1 |
exec Cleanup | ๋์ผ pagetable ๊ณต์ ํ๋ proc ์ ๋ถ freeproc() |
| join Collection | copyout()์ผ๋ก stack ์ฃผ์ ์ ์ ์๊ฒ ์ ๋ฌ |
kill() Extension | ๊ฐ์ pagetable ๊ฐ์ง ๋ชจ๋ proc์ ๋ํด killed = 1 ์ค์ |
| Synchronization | sleep() ์ p->lock ํ๋ณด, sched() ์ดํ wakeup ๊ฐ๋ฅ ๋ณด์ฅ |
์ด๋ฌํ ์ผ๊ด๋ ์ค๊ณ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก, xv6 ์์ ์์ ํ๊ณ ๊ฐ๋ฒผ์ด kernel thread ์์คํ
์ ๊ตฌํํ ์ ์์์ผ๋ฉฐ, ์ด๋ ๊ณผ์ ๋ช
์ธ์์ ์ ๊ณตํ thread_test.c์ ๋ชจ๋ ํ
์คํธ๋ฅผ ํต๊ณผํจ์ผ๋ก์จ ๊ทธ ๊ธฐ๋ฅ์ ์์ฑ๋๋ฅผ ์
์ฆํ์๋ค.
Implementation
Overall Structure Overview
์ด๋ฒ project๋ xv6์ kernel๊ณผ ์ฌ์ฉ์ ์์ญ ์์ชฝ์ ๊ฑธ์ณ thread ์์คํ ์ ๋ฐ์ ์์ฐ๋ฅด๋ ๊ตฌํ ๋ณ๊ฒฝ์ ํ์๋ก ํ๋ค. ๊ตฌํ์ ํฌ๊ฒ ์๋ 4๊ฐ์ง layer๋ก ๋๋๋ค.
- ์์คํ ์ฝ ์ธํฐํ์ด์ค (sysproc.c)
- kernel ๋ด๋ถ ๊ธฐ๋ฅ (proc.c)
- ์ฌ์ฉ์ ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ (user/thread.c, thread.h)
- ๋น๋ ์์คํ ๋ฐ ์คํ ํ์ผ ๊ตฌ์ฑ (Makefile ๋ณ๊ฒฝ)
๊ฐ ์์ญ์ ๋ํด ์ธ๋ถ ๊ตฌํ๊ณผ ํต์ฌ ๋ณ๊ฒฝ ํฌ์ธํธ๋ฅผ ์์๋๋ก ์ค๋ช ํ ๊ฒ์ด๋ค.
1. System Call Implementation (kernel/sysproc.c)
sys_clone()
uint64 sys_clone(void)
{
uint64 f, a1, a2, s;
argaddr(0, &f);
argaddr(1, &a1);
argaddr(2, &a2);
argaddr(3, &s);
return clone((void (*)(void*, void*))f, (void*)a1, (void*)a2, (void*)s);
}
argaddr()๋ ๋ฐํํ์ดvoid์ด๋ฏ๋ก,argraw()๊ฐ์ ๊ฐ์ ์ ์ผ๋ก ์ฐธ์กฐํ์ฌ ์ฒ๋ฆฌํ๋ค.- ์ธ์์ ์์์ ์๋ฏธ๋
clone(void(*fcn)(void*,void*), void*, void*, void*)์ ์ ํํ ์ผ์นํ๋๋ก ์ ๋ฌ๋๋ค. - ํจ์ ํฌ์ธํฐ ์ฃผ์(
fcn)๋ user space ๊ฐ์ด๋ฏ๋กuint64๋ก ์ฒ๋ฆฌํ๋ค.
sys_join()
uint64 sys_join(void)
{
uint64 p;
argaddr(0, &p);
return join((void**)p);
}
join()์ (void**) ํํ๋ก user stack address๋ฅผ ์ ๋ฌ๋ฐ๋๋ค.void **stackํํ๋ฅผuint64๋ก ๋ฐ๋, ์ปค๋ ๋ด๋ถ์์๋ type casting์ผ๋ก ๋ณต์ํ๋ค.
2. Kernel Internal Functions (kernel/proc.c)
clone()
allocproc()์ ํตํด ์๋ก์ด TCB๋ฅผ ํ ๋นํ๋ค.uvmshare()๋ก ๋ถ๋ชจ์ ๋์ผํ pagetable์ ๊ณต์ ํ๋ค.- trapframe์ ๋ถ๋ชจ์ ๊ฒ์ ๋ณต์ฌํ๊ณ , epc/sp/a0/a1 ๋ฑ์ ์ธ์๋ก ์ฃผ์ด์ง ๊ฐ์ ๋ฐ์ํด ์ค์ ํ๋ค.
ofile[]๊ณผcwd๋filedup(),idup()์ ํตํด ๋ณต์ฌํ๋ค.RUNNABLE๋ก ์ ํ๋๋ฉด scheduler์ ์ํด ์คํ๋๋ค.
np->trapframe->epc = (uint64)fcn;
np->trapframe->sp = (uint64)stack + PGSIZE;
np->trapframe->a0 = (uint64)arg1;
np->trapframe->a1 = (uint64)arg2;
np->ustack = (uint64)stack;์ join์์ stack ์ฃผ์๋ฅผ ์ ์ ์ ์ ๋ฌํ๊ธฐ ์ํด ๋ฐ๋ก ์ ์ฅํ๋ ๊ฐ์ด๋ค.
join()
proc[]๋ฐฐ์ด ์ ์ฒด๋ฅผ ์ํํ๋ฉฐ,parent == myproc()์ด๋ฉด์is_thread == 1์ธ ์์๋ง ํ์ธํ๋ค.state == ZOMBIE์ธ thread๋ฅผ ๋ฐ๊ฒฌํ๋ฉดfreeproc()์ผ๋ก ํ์ํ๊ณ , ๊ทธ thread์ustack๊ฐ์copyout()์ ํตํด ์ ์ ์๊ฒ ์ ๋ฌํ๋ค.- ์๊ฑฐํ thread๊ฐ ์์ผ๋ฉด sleep ์ํ๋ก ๋๊ธฐํ๋ค.
exec()
for(struct proc *q = proc; q < &proc[NPROC]; q++) {
if(q != p && q->pagetable == p->pagetable) {
acquire(&q->lock);
freeproc(q);
release(&q->lock);
}
}
exec์ ํธ์ถํ ํ์ฌ thread๋ฅผ ์ ์ธํ, ๊ฐ์ ์ฃผ์ ๊ณต๊ฐ์ ๊ณต์ ํ๋ thread๋ฅผ ๋ชจ๋ ์ ๊ฑฐํ๋ค.- ์ด ๋์์ ์๋ก์ด pagetable์ ์ธํ ํ๊ธฐ ์ ์ ๋ฐ๋์ ์ด๋ฃจ์ด์ ธ์ผ ํ๋ค.
kill()
for (p = proc; p < &proc[NPROC]; p++) {
if (p->pagetable == target->pagetable) {
p->killed = 1;
if (p->state == SLEEPING)
p->state = RUNNABLE;
}
}
- ๋จ์ผ
pid๊ฐ ์๋ ๋์ผ ์ฃผ์ ๊ณต๊ฐ(pagetable)์ ๊ณต์ ํ๋ ๋ชจ๋ proc์ ๋ํดkilled = 1์ค์ - ์ด๋ multi-thread ํ๊ฒฝ์์ ์ฌ๋ฐ๋ฅธ
killsemantics๋ฅผ ๋ณด์ฅํ๊ธฐ ์ํจ์ด๋ค.
fork()
- ๊ธฐ์กด๊ณผ ๊ฑฐ์ ๋์ผํ๋,
is_thread = 0์ผ๋ก ์ด๊ธฐํํ์ฌ ์์ ํ process ๋ณต์ ๊ฐ ๋๋๋ก ํ๋ค. - thread๊ฐ ์๋ process๋ ๋ฐ๋์ ๋ ๋ฆฝ์ ์ธ ์ฃผ์ ๊ณต๊ฐ์ ๊ฐ์ง๋ค.
3. User-Level Library (user/thread.c, user/thread.h)
thread_create()
void *s = sbrk(PGSIZE);
if (s == (void*)-1 || (uint64)s % PGSIZE) return -1;
return clone(f, a1, a2, s);
- Stack์ ๋ฐ๋์ 4KB ๋จ์๋ก page-aligned ๋์ด์ผ ํ๋ฉฐ, ๊ทธ๋ ์ง ์์ผ๋ฉด clone์ด ์คํจํ๋ค.
- stack ํ ๋น์ ๋์ ์ด๋ฉฐ, ์ ์ ๊ฐ ์ง์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ ๋นํ์ง ์์๋ ๋ด๋ถ์ ์ผ๋ก ์ฒ๋ฆฌ๋๋ค.
thread_join()
void *s = 0;
int pid = join(&s);
if (pid > 0 && s) sbrk(-PGSIZE);
return pid;
- join์ด ์ฑ๊ณต์ ์ผ๋ก ์ข
๋ฃ๋ thread์ stack ์ฃผ์๋ฅผ ์ ๋ฌํด์ฃผ๋ฉด, ์ด๋ฅผ
sbrk(-PGSIZE)๋ก ๋ฐ๋ฉํ์ฌ memory leak์ ๋ฐฉ์งํ๋ค.
thread.h
int thread_create(void (*fcn)(void*, void*), void *arg1, void *arg2);
int thread_join(void);
- ์ฌ์ฉ์ ๋ ๋ฒจ API๋ ๊ธฐ์กด pthread ์คํ์ผ์ ๋ชจ๋ฐฉํ์ฌ ์ต์ํ์ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
- ์ค์ ์์คํ ์ฝ์ ๋ด๋ถ์ ์ผ๋ก clone/join์ ํธ์ถํ๋ค.
4. Build Configuration Changes (Makefile.user)
$U/_thread_test: $U/thread_test.o $U/thread.o $(ULIB)
$U/_thread_fcn: $U/thread_fcn.o $(ULIB)
- ๋ณ๋๋ก
thread.o๋ฅผ ๋งํฌ์ ํฌํจํด์ผ thread ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ฐ๊ฒฐ๋๋ค.
entry("clone");
entry("join");
user/usys.pl๋๋ equivalent ํ์ผ์์ system call table์ clone๊ณผ join์ ๋ช ์์ ์ผ๋ก ์ถ๊ฐํด์ผ user code์์ ํธ์ถ ๊ฐ๋ฅํ๋ค.
Summary: Overall Flow
| Implementation Target | Role Summary |
|---|---|
clone() | ์๋ก์ด TCB(proc), ๊ณต์ ๋ ์ฃผ์ ๊ณต๊ฐ, ๋ ๋ฆฝ๋ trapframe ์์ฑ |
join() | ์ข ๋ฃ๋ thread ํ์ ๋ฐ stack ์ฃผ์ ๋ฐํ |
exec() | ๊ธฐ์กด thread ์ ๋ฆฌ ํ ์๋ก์ด ์ฃผ์ ๊ณต๊ฐ ๋ก๋ฉ |
kill() | thread ํฌํจ ๋ชจ๋ ์คํ ์ปจํ ์คํธ ์ข ๋ฃ |
thread_create() | ์คํ ํ ๋น + clone ์์คํ ์ฝ ๋ํ |
thread_join() | join ํธ์ถ ํ ์คํ ๋ฐ๋ฉ |
์ด๋ก์จ xv6 ๋ด์ ๊ฒฝ๋ํ๋ multithreading ํ๊ฒฝ์ด ๊ตฌ์ถ๋๋ฉฐ, fork์ exec์ ๊ธฐ์กด ์๋ฏธ๋ฅผ ์นจํดํ์ง ์์ผ๋ฉด์๋ thread ๊ธฐ๋ฐ ๋ณ๋ ฌ ์ฒ๋ฆฌ์ ์์ ๊ณต์ ๊ฐ ๊ฐ๋ฅํด์ง๋ค.
Results
๊ฐ test์์ ์์
์์ ์ ๊ณต๋ ์ถ๋ ฅ ์์์ ์ค์ ์คํ ๊ฒฐ๊ณผ์ ์ฐจ์ด๋ฅผ ๋น๊ตํ๊ณ , ๊ทธ ์ฐจ์ด๊ฐ ๋ฐ์ํ ์์ธ์ ๋ถ์ํ๋ฉฐ, ๊ฒฐ๊ณผ๊ฐ ๋
ผ๋ฆฌ์ ์ผ๋ก ํ๋นํ์ง๋ฅผ ์ค๋ช
ํ๋ค. ์๋๋ user/thread_test.c ์คํ ๊ฒฐ๊ณผ์ด๋ค. ์ด 6๊ฐ์ test๋ฅผ ๋ชจ๋ ํต๊ณผํ์์ผ๋ฉฐ, ๊ฐ๊ฐ์ test๋ ํน์ thread ๊ธฐ๋ฅ์ ์ ์ ์๋ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ค.
TEST#1: Thread Creation and Global Variable Sharing
Comparison
- ์์ ์ถ๋ ฅ์์๋ thread 1, 2, 3์ด ๋จผ์ ์คํ๋๊ณ ์ข ๋ฃ๋ ๋ค, thread 0์ด ๋ง์ง๋ง์ ์คํ๋๋ค.
- ์ค์ ๊ฒฐ๊ณผ์์๋ thread 0์ด ๊ฐ์ฅ ๋จผ์ ์คํ์ ์์ํ๊ณ , ๊ทธ ๋ค๋ก 1~4๋ฒ thread๊ฐ ์์ฐจ์ ์ผ๋ก ์คํ๋๋ค.
Analysis
- thread 0์ด ๋จผ์ ์คํ๋๋ ๊ฒ์ ์ค์ผ์ค๋ง ๋ฐฉ์์ ์ฐจ์ด์ ๋ฐ๋ฅธ ๊ฒ์ด๋ค.
- ๊ฐ thread์ ์คํ ์์์ ์ข ๋ฃ ์์ ์ ์์์ ๋ค๋ฅด์ง๋ง, ์ ์ญ ๋ณ์์ ์์ ๋ฐ main thread์์ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๋ ํ๋ฆ์ ๋์ผํ๊ฒ ์ ์ง๋๋ค.
Conclusion
- ๋ ผ๋ฆฌ์ ์ผ๋ก ๋ฌธ์ ๊ฐ ์์ผ๋ฉฐ, xv6์ round-robin scheduler์ ๋ฐ๋ผ ์คํ ์์๋ ๋ฌ๋ผ์ง ์ ์์ผ๋ฏ๋ก ์ถ๋ ฅ ์์์ ์ฐจ์ด๋ ํ์ฉ๋๋ ๊ฒ์ผ๋ก ๋ด์ผ ํ ๊ฒ์ด๋ค.
TEST#2: Argument Passing and Independent Execution
Comparison
- ์์์์๋ ๊ฐ thread๊ฐ ์ ๋ฌ๋ฐ์
iter๊ฐ์ ์ ํํ ์ถ๋ ฅํ๋ค. - ์ค์ ๊ฒฐ๊ณผ์์๋
iter๊ฐ์ด0๋ถํฐ4000๊น์ง ์ ํํ ์ถ๋ ฅ๋๋ค.
Conclusion
- ์ถ๋ ฅ ํ์๊ณผ ์์๊ฐ ๋ชจ๋ ์ผ์นํ๋ฉฐ, ๊ธฐ๋ฅ์ ์ผ๋ก๋ ์๋ฒฝํ ๊ตฌํ๋์๊ธฐ ๋๋ฌธ์ ๋ณ๋์ ์ค๋ช ์์ด ํต๊ณผ๋ก ํ๋จ๋๋ค.
TEST#3: Address Space Isolation After Fork
Comparison
- ์์์์๋ ๊ฐ thread๊ฐ forkํ ์์ process์์ "start" ๋ฐ "end" ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ค.
- ์ค์ ๊ฒฐ๊ณผ์์๋ ๋์ผํ ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ค.
Conclusion
- ์ ์ฒด ๊ตฌ์กฐ์ ํ๋ฆ์ด ๋์ผํ๋ฉฐ, ์ฃผ์ ๊ณต๊ฐ ์ถฉ๋ ์์ด fork ์ดํ ์์ thread๊ฐ ์ ์์ ์ผ๋ก ๋ถ๋ฆฌ๋ ๋ฉ๋ชจ๋ฆฌ์์ ์คํ๋์์์ ํ์ธํ ์ ์๋ค.
- ๋ถ๋ชจ thread๊ฐ ์์ ์ ์์ thread๋ฅผ ์คํ์์ผ ๋ณ๋ ฌ์ ์ผ๋ก ์ฒ๋ฆฌ
- ๋ชจ๋ ์์๊ณผ ๋ถ๋ชจ thread๊ฐ ์ ์ ์ข ๋ฃ
- ๊ฒฐ๊ณผ: nested thread ์์ฑ ๋ฐ ์คํ ์ ์ ์ฒ๋ฆฌ
TEST#4: sbrk and Memory Allocation Boundary Protection
Comparison
- ์์์์๋
addr n at break = 0x...ํํ์ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ค. - ์ค์ ๊ฒฐ๊ณผ์์๋ ์ฌ๋ฌ thread๊ฐ ์ ๊ทผํ๋ฉด์
usertrap๋ฐscause 0xf์๋ฌ ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ค.
Analysis
- ์์๋ user program์ด ์ง์ ํ ๋น ์ฃผ์๋ฅผ ์ถ๋ ฅํ ๋ฐ๋ฉด, ์ค์ ๊ตฌํ์์๋ ์ปค๋ ์์ค์์ ๋ฉ๋ชจ๋ฆฌ ๋ณดํธ ๊ธฐ์์ด ๋์ํ์ฌ trap์ด ๋ฐ์ํ๋ค.
- ์ด๋ thread๊ฐ ํ ๋นํ์ง ์์ page์ ์ ๊ทผํ์ ๋ ๋ณดํธ๊ฐ ๋ฐ์ํ๋์ง๋ฅผ ๊ฒ์ฆํ๊ธฐ ์ํ ํ ์คํธ์ด๋ฉฐ, ์์๋ ๋์์ด๋ค.
Conclusion
- ์คํ๋ ค trap์ด ๋ฐ์ํจ์ผ๋ก์จ ๋ณดํธ๋์ง ์์ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ์ ์ฐจ๋จํจ์ ์ฆ๋ช ํ์๊ณ , ์ด๋ test์ ์๋์ ๋ถํฉํ๋ฏ๋ก ํต๊ณผ๋ก ๊ฐ์ฃผํ ์ ์๋ค.
- thread 0์์
sbrk()๋ก ๋ฉ๋ชจ๋ฆฌ ํ์ฅ - ๊ฐ thread๋ ๊ณต์ ๋ ์ฃผ์ ๊ณต๊ฐ์ ๋ํ ์ ๊ทผ ์๋
- ์ฌ๋ฌ thread์์ ์ ๊ทผ ์ค๋ฅ(scause 0xf) ๋ฐ์ โ ์์๋ ํ๋
- ๊ฒฐ๊ณผ: ์ฃผ์ ๊ณต๊ฐ ๊ณต์ ์ memory boundary test ์ฑ๊ณต
TEST#5: Shared PID and Kill Behavior
Comparison
- ์์์์๋ thread 2๋ง ์กฐ๊ธฐ ์ข ๋ฃ๋๋ค.
- ์ค์ ๊ฒฐ๊ณผ์์๋ ๋ชจ๋ thread๊ฐ ๋์ผํ
pid๋ก ์์ํ๊ณ , thread 0์ ์ข ๋ฃ ๋ฉ์์ง๋ง ๋ช ์์ ์ผ๋ก ์ถ๋ ฅ๋๋ค.
Analysis
- ๋ชจ๋ thread๊ฐ ๋์ผํ
pid๋ฅผ ๊ณต์ ํ๋ ์ ์ ์์๋ ๊ฒฐ๊ณผ์ด๋ฉฐ, thread 2์ ์ข ๋ฃ ๋ก๊ทธ ๋๋ฝ์ scheduling timing ์ฐจ์ด์ผ ๊ฐ๋ฅ์ฑ์ด ๋๋ค. - join ์ดํ main์ด ์ข ๋ฃ๋์์ผ๋ฏ๋ก ๋๋จธ์ง thread๋ค๋ ํจ๊ป ์ข ๋ฃ๋ ๊ฒ์ด๋ค.
Conclusion
kill๊ด๋ จ ๋์์ ์ ๋๋ก ๋ฐ์๋์๊ณ , ๋ชจ๋ thread๊ฐ ๋์ผํ process์ ์ผ๋ถ๋ก ์ ์ ์ข ๋ฃ๋์์ผ๋ฏ๋ก ๊ธฐ๋ฅ ๊ตฌํ์ ๋ฌธ์ ๋ ์๋ค.- ๋ชจ๋ thread๊ฐ ๋์ผํ
PID(29)๋ก ์ถ๋ ฅ๋จ - ์ด๋ thread๊ฐ ๋ ๋ฆฝ์ ์ธ process๊ฐ ์๋์ ์ฆ๋ช
- ๊ฒฐ๊ณผ: ๋ชจ๋ thread๋ ๋์ผํ process ๋ด์์ ์คํ๋จ
TEST#6: exec and Thread Termination Behavior
Comparison
- ์์์์๋ exec ์ดํ "thread exec test 0" ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ค.
- ์ค์ ๊ฒฐ๊ณผ์์๋ ์ผ๋ถ thread๊ฐ ๋จผ์ trap์ ๋ฐ์์ํค๊ณ ์ดํ ๋์ผํ ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ค.
Analysis
- exec์ด ํธ์ถ๋๋ฉด ์ฃผ์ ๊ณต๊ฐ์ด ์๋ก์ด program image๋ก ๋์ฒด๋๋ฉฐ, ๊ธฐ์กด์ ๋ชจ๋ thread๋ ์ข ๋ฃ๋๋ค.
- ๊ทธ์ ๋ฐ๋ผ ์ ๊ทผ ๋ถ๊ฐ๋ฅํ ๊ณต๊ฐ์ ์๋ thread๋ค์ด trap์ ๋ฐ์์ํจ ๋ค, ์๋ก ์คํ๋ image๊ฐ ์ ์์ ์ผ๋ก ์คํ๋ ๊ฒ์ด๋ค.
Conclusion
- ๋ชจ๋ trap์ exec์ ๋ฐ๋ฅธ ์๋๋ ๋ถ์์ฉ์ด๋ฉฐ, exec ์ดํ program์ด ์ ์ ์คํ๋์๊ธฐ ๋๋ฌธ์ ์ฌ๋ฐ๋ฅธ ๋์์ผ๋ก ํ๋จ๋๋ค.
thread 0์ดexec()์ ํธ์ถํ์ฌ ์๋ก์ด ํ๋ก๊ทธ๋จ์ผ๋ก ๋ณํ- ๋ค๋ฅธ thread๋ค์ ์์๋๋ก ๋น์ ์ ์ข
๋ฃ (scause
0x2/0xd) exec()์ดํ ์๋ก์ด ํ๋ก๊ทธ๋จ์ด ์ ์์ ์ผ๋ก ์คํ๋จ์ ํ์ธ- ๊ฒฐ๊ณผ: exec ์ดํ ์ ์ฒด process context ๊ต์ฒด ์ฑ๊ณต
Final Conclusion
- ์ด 6๊ฐ์ ํ ์คํธ ๋ชจ๋ ํต๊ณผ
- xv6์ ์ปค๋ ์์ค์ thread๋ฅผ ์์ ์ ์ผ๋ก ๋์ ๋ฐ ๊ฒ์ฆ ์๋ฃ
- ์ฃผ์ ๊ธฐ๋ฅ(์์ฑ, ์ข
๋ฃ, ๋ฉ๋ชจ๋ฆฌ ๊ณต์ , PID ์ผ์น,
exec์ฒ๋ฆฌ ๋ฑ) ์ ๋ฐ์ ์ผ๋ก ๊ธฐ๋ ๋์ ์ํ - ์์ ์์ ์ ๊ณต๋ ์์ ์ถ๋ ฅ๊ณผ ์คํ ์์ ๋๋ ๋ฉ์์ง๊ฐ ์ผ๋ถ ๋ค๋ฅด์ง๋ง, ์ด๋ xv6์ scheduler ํน์ฑ์ ์์ฐ์ค๋ฌ์ด ์ฐจ์ด์ด๋ค.
- ๋ชจ๋ test์ ํต์ฌ ๊ธฐ๋ฅ์ ์ ํํ ๊ตฌํ๋์์ผ๋ฉฐ, trap ๋ฐ์ ๋ํ ๋ฉ๋ชจ๋ฆฌ ๋ณดํธ ๊ธฐ๋ฒ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ์์ ์๋ฏธํ๋ค.
- ๋ฐ๋ผ์
"All tests passed"๋ผ๋ ์ถ๋ ฅ์ ๋ ผ๋ฆฌ์ ์ผ๋ก ํ๋นํ๊ณ , ๋ชจ๋ ์๊ตฌ ์กฐ๊ฑด์ ์ถฉ์กฑํ๋ค๊ณ ํ๋จํ ์ ์๋ค.

Troubleshooting
thread ๊ตฌํ์ ์ ํต์ ์ธ process์ ๋ฌ๋ฆฌ ์ฃผ์ ๊ณต๊ฐ ๊ณต์ , ์ปจํ ์คํธ ๋ถ๋ฆฌ, ์์ ํ์ ํ์ด๋ฐ ๋ฑ ๋ค์ธต์ ์ธ ๋ฌธ์ ๋ฅผ ์๋ฐํ๋ค. ๋ณธ project ์งํ ๊ณผ์ ์์ ๋ฐ์ํ๋ ๋ํ์ ์ธ ๋ฌธ์ ์ฌ๋ก๋ค๊ณผ, ๊ทธ์ ๋ํ ์์ธ ๋ถ์ ๋ฐ ํด๊ฒฐ ๊ณผ์ ์ ๊ธฐ์ ํ๋๋ก ํ๋ค.
1. clone() Infinite Thread Creation โ PID Explosion
Issue
thread_create()๊ฐ ์ฌ๋ฌ ๋ฒ ํธ์ถ๋๋ฉฐclone()์ด ์คํจํ์์๋ ๋ถ๊ตฌํ๊ณ ๊ณ์ํด์ pid๊ฐ ์ฆ๊ฐpid๊ฐ 64๋ฅผ ์ด๊ณผํ๋ฉฐ ์์คํ ์ด ๋น์ ์ ์ํ์ ์ง์usertrap(): unexpected scause๋๋kerneltrap()์ด ๋ค์ ๋ฐ์
Cause
thread_create()์์sbrk(PGSIZE)๋ก stack์ ์์ฒญํ์ผ๋ ์คํจํ ๊ฒฝ์ฐ, ๊ทธ stack ์ฃผ์๊ฐ(void*)-1์ด ๋จ- ์ด ์ํ๋ก๋
clone()์ด ํธ์ถ๋๋ฉด, ์ปค๋ ๋ด stack validation์์ ์คํจํ์ฌ-1์ ๋ฐํํ์ง๋ง, ์ฌ์ฉ์ ์ฝ๋๋ ์ด๋ฅผ ๋ฌด์ํจ - ๊ทธ ๊ฒฐ๊ณผ join์ด ๋ถ๊ฐ๋ฅํ zombie thread๊ฐ ๊ณ์ ์์ฑ๋์ด
proc[]์ ์์งํจ
Solution
thread_create()์์ stack์ด ํ ๋น ์คํจํ๊ฑฐ๋ page alignment๊ฐ ๋ง์ง ์์ผ๋ฉด ์ฆ์ ์คํจ ์ฒ๋ฆฌํ๋๋ก ๋ณ๊ฒฝ
void *s = sbrk(PGSIZE);
if (s == (void*)-1 || (uint64)s % PGSIZE) return -1;
clone()๋ด๋ถ์์๋ stack์ด NULL์ด๊ฑฐ๋ ๋ฒ์ ๋ฐ์ธ ๊ฒฝ์ฐ ์์ธ ์ฒ๋ฆฌ ๊ฐํ
if (!stack || (uint64)stack < PGSIZE || ... ) return -1;
2. Error on fcn == 0 โ Test Failure
Issue
clone()๋ด์์if (!fcn) return -1;์กฐ๊ฑด์ ์ค์ ํ ํ, ๋ชจ๋ thread ์์ฑ์ด ์คํจthread_test์์Thread 0 start์กฐ์ฐจ ์ถ๋ ฅ๋์ง ์์
Cause
- ์์
์์ ์ ๊ณตํ test code์์
thread_basic()ํจ์์ ์ฃผ์๊ฐ ์ค์ ๋ก0x0์ผ๋ก ์ค์ ๋์ด ์์ - ์ด๋ xv6์ linker ๋ฐ loader ์ค์ ์ ์ผ๋ถ ํจ์๊ฐ
.text์ธ๊ทธ๋จผํธ์ ์์ ์ฃผ์์ธ0x0์ ๋ฐฐ์น๋๊ธฐ ๋๋ฌธ - xv6 riscv ํ๊ฒฝ์์๋
epc = 0์ด trap ์์ด ์ ํจํ๊ฒ ์คํ๋์ด์ผ ํ๋ฉฐ,0x0์ฃผ์๋ ์ ์์ ์ธ ์คํ ์ง์ ์ ์
Solution
clone()๋ดfcnnull-check ์กฐ๊ฑด์ ์์ ํ ์ ๊ฑฐ- ๋์
walkaddr()๋ฅผ ํตํด ํด๋น address๊ฐ ์ ์ ์์ญ์ ์กด์ฌํ๋ ์ ํจํ physical page๋ฅผ ๊ฐ๋ฆฌํค๋์ง๋ง ํ์ธ
if ((uint64)fcn >= MAXVA || !walkaddr(p->pagetable, (uint64)fcn)) return -1;
3. join Unable to Collect Thread (Infinite Wait)
Issue
join()์ ํธ์ถํ์ผ๋Thread X join failed๊ฐ ์ถ๋ ฅ๋๋ฉฐ ํ ์คํธ ์คํจ- thread๋ ์ข ๋ฃ๋์์ผ๋ ์ปค๋์ด ํด๋น ์ํ๋ฅผ ์ธ์งํ์ง ๋ชปํจ
Cause
- thread ์ข
๋ฃ ์
state == ZOMBIE๊ฐ ๋์์ง๋ง,join()์์ ์ ๊ทผ ์np->lock์ ํ๋ํ์ง ์๊ฑฐ๋, ์ด๋ฏธfreeproc()์ดํ lock์ ํด์ ํ ๋ค ์ํ๋ฅผ ํ์ธํ๋ ค ํด race condition์ด ๋ฐ์ - ๋๋
p->lock๊ณผwait_lock์ ๋์์ ์ฌ์ฉํ๋ฉด์ deadlock์ด ๋ฐ์ํ ์ ์์
Solution
join()๋ด์์ ๋ฐ๋์np->lock์ ํ๋ํ ๋คstate๋ฅผ ํ์ธfreeproc()๋ ๋ฐ๋์np->lock์ ๋ณด์ ํ ์ํ์์ ํธ์ถํ๊ณ , ํด์ ๋ ์ดํ์ ์ํ- sleep์ ๋ฐ๋์
p->lock์์ดwait_lock๋ง ๋ณด์ ํ ์ํ์์๋ง ํธ์ถํด์ผ ํจ
4. Other Threads usertrap on exec() โ panic
Issue
exec()ํ ์คํธ(TEST#6) ์ํ ์, ๋ค๋ฅธ thread๋ค์์ ๋ค์๊ณผ ๊ฐ์ log ์ถ๋ ฅ
usertrap(): unexpected scause 0x2 pid=36 sepc=0x1000 stval=0xdeadbeef
- ์ดํ ์ปค๋ panic ๋๋ stack overflow ๋ฐ์
Cause
exec()์ ํธ์ถ thread์ pagetable์ ์์ ํ ์๋ก์ด ๊ฒ์ผ๋ก ๊ต์ฒดํ๋ ์์ ์ด๋ค.- ๊ทธ๋ฌ๋ ๊ฐ์ pagetable์ ๊ณต์ ํ๋ ๋ค๋ฅธ thread๋ค์ด ์์ง ์ด์ ์๊ณ , ํด๋น ์ฃผ์๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ช ๋ น์ด๋ฅผ fetchํ๋ ค๊ณ ํ๋ฉด invalid memory trap์ด ๋ฐ์
- ํนํ, ์ด๋ฏธ ์๋ฉธ๋ ํจ์ ์ฃผ์(
fcn)๋ก ์ ํํ๋ ๋์ค ๋ฌธ์ ๊ฐ ๋ฐ์
Solution
exec()ํธ์ถ ์ ํ์ฌ thread๋ฅผ ์ ์ธํ ๋์ผ pagetable์ thread๋ฅผfreeproc()์ผ๋ก ์ ๊ฑฐ
for(struct proc *q = proc; q < &proc[NPROC]; q++) {
if(q != p && q->pagetable == p->pagetable) {
acquire(&q->lock);
freeproc(q);
release(&q->lock);
}
}
- ๊ฒฐ๊ณผ์ ์ผ๋ก,
exec()๋ ํด๋น process๋ฅผ thread ๋จ์๊ฐ ์๋ "์ฃผ์ ๊ณต๊ฐ ๋จ์"๋ก ์์ ํ ์ฌ์์ํ๋ ํจ์๋ก ๋ณด์์ผ ํ๋ค.
5. Only Some Threads Terminated on kill(pid) โ zombie leak
Issue
kill(main_pid)ํธ์ถ ์ ์ผ๋ถ thread๋ ์ฌ์ ํ ๋๊ณ ์์ผ๋ฉฐ,join()๋ ๋ฐํํ์ง ์์TEST#5์์ thread ์ค ํ๋๋ง ์ข ๋ฃํ๊ณ ๋๋จธ์ง๋ ๋ฌดํ ๋ฃจํ์ ๋น ์ง
Cause
- ๊ธฐ์กด
kill()๊ตฌํ์pid์ ์ผ์นํ๋ ๋จ์ผ process๋ง ์ข ๋ฃ ์ฒ๋ฆฌํจ - ๊ทธ๋ฌ๋ thread ๊ตฌ์กฐ์์๋ ํ๋์ ์ฃผ์ ๊ณต๊ฐ์ ์ฌ๋ฌ thread๊ฐ ์กด์ฌํ๋ฉฐ, ๊ฐ๋ณ pid๋ง ์ข ๋ฃํด์ ํจ๊ณผ๊ฐ ์์
Solution
kill()๊ตฌํ์ ํ์ฅํ์ฌ ๋์ผํ pagetable์ ๊ณต์ ํ๋ ๋ชจ๋ proc์ ๋ํดkilled = 1์ค์ SLEEPING์ํ์ผ ๊ฒฝ์ฐ, ๊ฐ์ ๋กRUNNABLE๋ก ๋ง๋ค์ดsched()์ ์ง์ ํ ์ ์๋๋ก ํจ
for (p = proc; p < &proc[NPROC]; p++) {
if (p->pagetable == target->pagetable) {
p->killed = 1;
if (p->state == SLEEPING)
p->state = RUNNABLE;
}
}
6. Stack Memory Leak after thread_join()
Issue
- thread ์๊ฑฐ ์ดํ์๋
sbrk()๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ ํ์๋์ง ์์,sbrk(0)๊ฐ์ด ๊ณ์ ์ฆ๊ฐ - long-running process์์ memory exhaustion ๋ฐ์
Cause
join()์ด stack ์ฃผ์๋ฅผ ์ ์ ์๊ฒ ๋๊ฒจ์คฌ์ง๋ง, ์ฌ์ฉ์๊ฐ ์ด๋ฅผ ๋ฐ๋ฉํ์ง ์์thread_join()์join()์ ํธ์ถ๋ง ํ๊ณ ๋ฉ๋ชจ๋ฆฌ ํ์๋ ํ์ง ์์
Solution
- ์ฌ์ฉ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ค์์
thread_join()๋ด๋ถ์์ ์ง์ sbrk(-PGSIZE)ํธ์ถ - stack ์ฃผ์๊ฐ 0์ด ์๋ ๊ฒฝ์ฐ์๋ง ํด์
if (pid > 0 && s) sbrk(-PGSIZE);
7. sleep/wakeup Deadlock
Issues
join()์์sleep(p, &wait_lock)์ดํ ๊นจ์ด๋์ง ์๊ฑฐ๋, wakeup ์ดํ์๋ ์ํ๊ฐ ๋ณต๊ตฌ๋์ง ์์- Debugging ์
sched()์ง์ ํ ๋ค์ ๋์์ค์ง ์๊ฑฐ๋panic: unlock๋ฐ์
Cause
sleep()์ ๋ด๋ถ์ ์ผ๋กp->lock์ ์๊ตฌํ๋ฉฐ, ์ธ๋ถ์์ ์ด๋ฏธp->lock์ ํ๋ํ ์ํ๋ก ๋ค์ด์ค๋ฉด ์ค๋ณต lock์ผ๋ก crash- ๋๋
wakeup()๊ณผsleep()์ฌ์ด์ window์์ race condition์ด ๋ฐ์ํ ์ ์์
Solution
sleep()ํธ์ถ ์ ๋ฐ๋์p->lock์ด ํ๋๋์ด ์์ง ์๋๋ก ์ค๊ณsleep()๋ด๋ถ์์p->lock์ ํ๋ํ๋ฉฐ, ์ธ๋ถ์์๋wait_lock๋ง ๋ณด์ ํด์ผ ํจ
acquire(&p->lock);
release(&wait_lock);
...
sched();
...
release(&p->lock);
acquire(&wait_lock);
wakeup()์ lock ์์ด ์๋ํด์ผ ํ๋ฏ๋ก ๋ณ๋๋กp->lock์ ๊ฐ thread๋ง๋ค ํ๋ํ๊ณ ์ํ๋ฅผ ๋ฐ๊ฟ์ค
Summary: Major Issues List
| Issue Code Area | Cause | Solution Summary |
|---|---|---|
thread_create | sbrk ์คํจ ๋๋ misalignment | ์คํจ ์ ๋ฐ๋ก return |
clone | fcn == 0 ์ฃผ์ ๊ฑฐ๋ถ | fcn null-check ์ ๊ฑฐ, walkaddr๋ง์ผ๋ก ์ ํจ์ฑ ํ์ธ |
join | lock ๋ณดํธ ๋ฏธํก | lock ์์ ๋ฐ freeproc() ๋ด๋ถ ๋๊ธฐํ ๋ณด๊ฐ |
exec | ๋ค๋ฅธ thread๊ฐ trap ๋ฐ์ | ๋์ผ pagetable ๊ณต์ thread ์ ๋ถ ์ ๊ฑฐ |
kill | ์ผ๋ถ thread๋ง ์ข ๋ฃ๋จ | pagetable ๊ธฐ์ค ์ ์ฒด killed ์ฒ๋ฆฌ |
thread_join | stack ํ์ ๋๋ฝ | ๋ด๋ถ์์ sbrk(-PGSIZE) ํธ์ถ ์ถ๊ฐ |
sleep/wakeup | deadlock ๋๋ race condition | lock ์์ ๋ฐ wakeup ์์ ์กฐ์ |
์ด์ ๊ฐ์ ๋ฐ๋ณต์ debugging๊ณผ ์ฝ๋ ๊ฐ์ ์ ํตํด, xv6์ ์์ ์ ์ด๊ณ ์ ํํ kernel ์์ค thread ์์คํ ์ ๊ตฌํํ ์ ์์๋ค.
Additional Content
Reference Notes for Thread Implementation in xv6 RISC-V
๋ณธ project๋ xv6 RISC-V ๋ฒ์ ์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฉฐ, x86 ๊ธฐ๋ฐ ๊ตฌํ ์์ ๋ค๊ณผ๋ ๊ตฌ์กฐ์ ์ผ๋ก ๋ค๋ฅด๋ค.
- ํนํ
ptable[]์ด ์กด์ฌํ์ง ์๊ณ ,proc[NPROC]๋ฐฐ์ด๊ณผ spinlock์ผ๋ก๋ง ๋๊ธฐํ๋ฅผ ์ํํ๋ค. - ๋ฐ๋ผ์ x86์
switchuvm(),loaduvm()๋ฑ์ ๋์ ๋ฐฉ์๊ณผ ๋ฌ๋ฆฌ, RISC-V์์๋walk()๋ฐpagetable_t์ ์ง์ ์ ๊ทผํ๋ ๋ฐฉ์์ด ํ์ํ๋ค.
- ํนํ
xv6์ trapframe, context, scheduler ๋ฑ์ thread-safeํ์ง ์์ผ๋ฏ๋ก, thread๋ง๋ค ๋ฐ๋์ ๋ ๋ฆฝ๋
trapframe,context,ustack์ ์ฌ์ฉํด์ผ ํ๋ค.uvmshare()๋ ๊ธฐ์กดuvmcopy()๋ฅผ ๋ณํํ์ฌ ๊ตฌํํ ํจ์๋ก,PTE_U๋นํธ๊ฐ ์ค์ ๋ ์ฌ์ฉ์ ์์ญ page์ ๋ํด์๋ง ๊ณต์ (reference mapping)ํ๋๋ก ์ค๊ณํ์๋ค.- ์ด๋ read-only ์ธ๊ทธ๋จผํธ์ ๋ํด์๋ ๋ฌธ์ ๊ฐ ์์ผ๋ฉฐ, exec์ด๋ fork์๋ ๋ค๋ฅธ ๋์์ ๋ณด์ฅํ๋ค.
thread_fcn.c,thread_test.c๋ ๊ณผ์ ๋ช ์ธ์์ ์ ๊ณตํ ์ฑ์ ๊ธฐ์ค ํ์ผ์ด๋ค.- ์ด ํ์ผ์ ๊ตฌ์กฐ๋
fcn = 0x0ํํ์ ํธ์ถ ๋ฐฉ์์ ์ ์ ๋์์ด๋ฉฐ, panic ์ ๋ฐ ์์๊ฐ ์๋๋ค.
- ์ด ํ์ผ์ ๊ตฌ์กฐ๋
๋ชจ๋ thread ๊ตฌํ์ xv6์ ๊ธฐ์กด ๊ธฐ๋ฅ๊ณผ ์์ ํ ํธํ๋์ด์ผ ํ๋ฉฐ, ํนํ ๋ค์๊ณผ ๊ฐ์ system call๋ค๊ณผ ์ฐ๋ ๊ฐ๋ฅํด์ผ ํ๋ค.
fork(),exec(),exit(),wait(),kill(),sleep(),pipe()
Additional Useful References
https://github.com/mit-pdos/xv6-riscv
https://pdos.csail.mit.edu/6.828/2022/xv6/book-riscv-rev3.pdf
์ค์ต ์ค ์ฐธ๊ณ ํ StackOverflow thread:
- https://stackoverflow.com/questions/78141535/threading-using-jmp-buf-array-in-c-test-in-xv6 (x86 ์ค์ฌ์ด๋ฏ๋ก ๊ตฌ์กฐ๋ ์ฐธ์กฐ์ฉ)
์ด์์ผ๋ก ๋ณธ project์ ๊ธฐ์ ์ ๋ฐฐ๊ฒฝ๊ณผ ์ฐ๊ด๋ ๋ชจ๋ ํต์ฌ ์์๋ค์ ํฌํจํ์๋ค.

