Project 3: Virtual Memory & File system - wiki
1. Design
1.1. Project overview
์ด wiki๋ xv6-riscv ๊ธฐ๋ฐ ์ด์์ฒด์ ์ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ์ ํ์ผ ์์คํ
๊ธฐ๋ฅ์ ํ์ฅํ ๊ณผ์ ์ ์ ๋ฆฌํ๋ค.
๋ณธ Project์ ๋ชฉํ๋ ๋ฉ๋ชจ๋ฆฌ ํจ์จยท์ ์ฅ ์ฉ๋ยทํ์ผ ์ฐธ์กฐ ์ ์ฐ์ฑ์ ๊ฐ์ ํ๋ ๊ฒ์ด๋ค.
- copy-on-write(COW) fork
- ๊ธฐ์กด xv6๋
fork์ ์ฌ์ฉ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ ๋ถ ๋ณต์ฌ โ ๊ณผ๋ํ ๋ฉ๋ชจ๋ฆฌ ์๋น - ํด๊ฒฐ: parent-child๊ฐ page๋ฅผ ๊ณต์ ํ๊ณ ์ฒซ write ์์๋ง ๋ณต์ฌ
- ๊ธฐ์กด xv6๋
- large file support
- ์ต๋
268 KBํ๊ณ(12 direct + 1 indirect) - ํด๊ฒฐ: double-indirect ๋ ์ด์์ ๋์ โ ์ฝ 66 MB๊น์ง ํ์ผ ์์ฑยท์ ๊ทผ ๊ฐ๋ฅ
- ์ต๋
- symbolic link
- hard link๋ง ์ ๊ณต, ๊ฒฝ๋ก-๊ธฐ๋ฐ ๋งํฌ๊ฐ ๋ถ์ฌ
- ํด๊ฒฐ: UNIX-style symlink ๊ตฌํ + link-chain ํด์(๊น์ด ์ ํ 10๋จ๊ณ)
| ๊ธฐ๋ฅ | ๊ธฐ์กด ํ๊ณ | ํ์ฅ ๋ชฉํ |
|---|---|---|
| Copy-on-Write | fork ์ ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ๋ณต์ฌ | ๊ณต์ ํ ์ฒซ write ๋๋ง ๋ณต์ฌ |
| Large file | 268 KB ์ต๋ ํฌ๊ธฐ | 66 MB ์ง์(double-indirect) |
| Symbolic link | hard link๋ง ์ง์ | ๊ฒฝ๋ก ๊ธฐ๋ฐ symlink + loop ๋ฐฉ์ง |
๋ณธ ๊ตฌํ์ xv6์ ์ํ ๊ตฌ์กฐ๋ฅผ ์ ์งํ๋ฉด์ ์์ฐ์ค๋ฝ๊ฒ ๊ธฐ๋ฅ์ ํ์ฅํ์์ผ๋ฉฐ, ๊ฐ ๊ธฐ๋ฅ๋ณ ํ
์คํธ ํ๋ก๊ทธ๋จ์ ํตํด ์์ ์ฑ๊ณผ ์ ํ์ฑ์ ํ์ธํ์๋ค.
์ด wiki๋ ํ
์คํธ ์ค๋ช
โ ๊ตฌํ ์ธ๋ถ ์ฌํญ โ ๊ฒฐ๊ณผ ๋ฐ ๋ฌธ์ ํด๊ฒฐ ์์๋ก ์์ ํด ์ต์ข
์์ฑ๋๋ฅผ ์
์ฆํ๊ฒ ๋๋ค.
1.2. Design principles and requirement strategy
1.2.1. On-demand page duplication
uvmcopy์์ writable page์PTE_W๋ฅผ ์ ๊ฑฐํ๊ณPTE_COW๋ฅผ ์ธํ ํด parent, child๊ฐ ๊ฐ์ physical page๋ฅผ read-only๋ก ๊ณต์ ํ๋ค.- write fault๋
usertrap์์ ๊ฐ์งํ๋ฉฐ,ref-count > 1์ด๋ฉด ์ page๋ฅผkallocํ ๋ณต์ฌ,ref-count == 1์ด๋ฉดflag๋ง ๊ฐฑ์ ํด write-enable ํ๋ค.
1.2.2. Page reference accounting
ref_counts[PHYSTOP/PGSIZE]์ ์ญ ๋ฐฐ์ด๋ก ๋ชจ๋ physical page ์ฌ์ฉ ๊ฐ์๋ฅผ ์ถ์ ํ๋ค.inc_ref/kfree๋ก ์ฆ๊ฐยท๊ฐ์ํ๋ฉฐ 0์ผ ๋๋ง free list์ ๋ฐํํด leak์ ๋ฐฉ์งํ๋ค.
1.2.3. TLB consistency on permission change
- RISC-V์๋ x86 CR0.WP๊ฐ ์์ผ๋ฏ๋ก, write-protect ํ ๋ฐ๋์
sfence_vma๋ฅผ ํธ์ถํด ์ฌ์ฉ์ TLB๋ฅผ flushํ๋ค.
1.2.4. Double-indirect block layout
NDIRECT๋ฅผ 11๋ก ์ค์ด๊ณaddrs[11](single),addrs[12](double) ๋ ํฌ์ธํฐ๋ฅผ ์์ฝํ๋ค.bmap๋ three-tier lookup (direct โ single โ double) ๋ก์ง์ ๊ฐ์ง๋ค.MAXFILE = 11 + 256 + 256ยฒ๊ณ์ฐ์ผ๋ก bigfile ํ ์คํธ(65 803 blocks)๋ฅผ ์ปค๋ฒํ๋ค.
1.2.5. Metadata integrity via write-ahead logging
- indirect ํ
์ด๋ธ์ ์์ ํ ๋๋ง๋ค
log_write(bp)ํธ์ถ ํbrelse(bp)๋ก ํด์ ํด log ์ผ๊ด์ฑ์ ๋ณด์ฅํ๋ค.
1.2.6. Safe truncation path
itrunc๋ direct โ single โ double ์ธ ๋จ๊ณ์ ๋ํด ์ญ์์ผ๋กbfree๋ฅผ ํธ์ถํด orphan block์ ๋จ๊ธฐ์ง ์๋๋ค.
1.2.7. Path-level symbolic link
- ์๋ก์ด inode type
T_SYMLINK์ ์ถ๊ฐํ๊ณ , link ํ์ผ์ target ๊ฒฝ๋ก ๋ฌธ์์ด์ ์ ์ฅํ๋ค. sys_open์ ์ต๋MAX_SYMLINK_LOOPS (10)๊น์ด๊น์ง ์ฌ๊ท ํด์ํ๋ฉฐ,O_NOFOLLOW๊ฐ ์์ผ๋ฉด ๋งํฌ ์์ฒด๋ฅผ ์ฐ๋ค.
1.2.8. Loop and broken-link defense
- depth ์นด์ดํฐ๋ก ์ํ ๋งํฌ๋ฅผ ์ฐจ๋จํ๊ณ , target ๋ฏธ์กด์ฌ ์ open ์คํจ๋ฅผ ๋ฐํํ๋ค.
1.2.9. Concurrency safety
- symlink ์์ฑยท์ญ์ ๋
begin_op()/end_op()ํธ๋์ญ์ ๋ด๋ถ์์ inode lock์ ๋๊น์ง ์ ์งํด race condition์ ๋ฐฉ์งํ๋ค.
1.3. Design key summary
| Element | Implementation strategy summary |
|---|---|
| Page sharing | uvmcopy โ PTE_COW flag + read-only |
| Fault handling | usertrap branch for scause==store fault |
| Ref count | inc_ref, get_ref, kfree guard == 0 |
| Double-indirect layout | addrs[NDIRECT+2] + 3-level bmap |
| MAXFILE ๊ณ์ฐ | 11 + 256 + 256ยฒ = 65 803 blocks |
| Log consistency | ๊ฐ level ๋ณ๊ฒฝ ์งํ log_write(bp) |
| Symlink syscall | sys_symlink(target, path) + T_SYMLINK inode |
| Link resolution | sys_open loop, depth โค 10, O_NOFOLLOW support |
| Concurrency | inode lock + write-ahead log around link ops |
์ด ์ค๊ณ์ ๋ฐ๋ผ COW, large file, symbolic link ์ธ ๊ธฐ๋ฅ์ ๊ธฐ์กด xv6 ์ฝ๋์ ํธํ๋๋๋ก ํตํฉํ์ผ๋ฉฐ, ๊ฐ๊ฐ cowtest, bigfile, symlinktest๋ฅผ ์์ ํต๊ณผํ๊ณ ๋ชฉํ ์๊ตฌ์ฌํญ์ ์ถฉ์กฑํ๊ฒ ๋๋ค.
2. TEST ํ์ผ์ ๋ํ ์ค๋ช
2.1. /user/cowtest.c
์ด ํ๋ก๊ทธ๋จ์ copy-on-write fork ๊ธฐ๋ฅ์ ํ ์คํธํ๋ user space ํ๋ก๊ทธ๋จ์ด๋ค.
์ฃผ์ ํ ์คํธ ํจ์๋ ๋ค์๊ณผ ๊ฐ๋ค.
simpletest
์ ์ฒด physical memory์ 2/3 ๊ฐ๋์sbrk๋ก ํ ๋นํ๊ณfork๋ฅผ ์ํํ์ฌ, parent์ child๊ฐ page๋ฅผ ๊ณต์ ํ๋์ง ํ์ธํ๋ค. copy-on-write์ด ๊ตฌํ๋์ด ์์ง ์์ผ๋ฉดfork์คํจ๊ฐ ๋ฐ์ํ๋ค.threetest
3๊ฐ์ process๊ฐ ๊ฐ์ COW page์ write๋ฅผ ์ํํ๋ฉฐ ์ค์ ๋ก page๊ฐ copy๋๊ณ free๋๋์ง๋ฅผ ํ์ธํ๋ค. ๋ด์ฉ ๋ถ์ผ์น๋ memory leak์ด ์์ด์ผ ํต๊ณผ๋๋ค.filetestpipe์fork๋ฅผ ํตํดcopyout๋์ ์ค page fault๊ฐ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋๋์ง๋ฅผ ๊ฒ์ฆํ๋ค. parent์buf๊ฐ child์ ์ํด ์ค์ผ๋์ง ์์์ผ ์ฑ๊ณต์ด๋ค.
uint64 phys_size = PHYSTOP - KERNBASE;
int sz = (phys_size / 3) * 2;
char *p = sbrk(sz);
int pid = fork();
์ด ํ
์คํธ๋ค์ fork์ ์ฑ๋ฅ ์ต์ ํ์ memory isolation์ ์ ํ์ฑ์ ์ ๊ฒํ๋ ๋ฐ ํต์ฌ์ ์ธ ์ญํ ์ ํ๋ค.
2.2. /user/bigfile.c
์ด ํ
์คํธ ํ๋ก๊ทธ๋จ์ xv6 ํ์ผ system์์ large file์ ์์ฑํ๊ณ , ๋ฐ์ดํฐ๋ฅผ block ๋จ์๋ก ์ฐ์์ ์ผ๋ก ์ฐ๊ณ readํ์ฌ data integrity๋ฅผ ๊ฒ์ฆํ๋ ๊ธฐ๋ฅ์ ์ํํ๋ค.
์ฃผ์ ๋์ ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
"big.file"์ด๋ผ๋ ์ด๋ฆ์ผ๋ก file์O_CREATE | O_WRONLYflag๋ฅผ ์ฌ์ฉํดopenํ๊ณwrite๋ฅผ ์ํํ๋ค.- block ํฌ๊ธฐ(
BSIZE)๋งํผbuf๋ฅผ ์ค๋นํ๊ณ , ๊ฐ block์ ๋ฒํธ๋ฅผ ๊ธฐ๋กํ ํ ๋ฐ๋ณต์ ์ผ๋ก file์ ์ด๋ค. - ์ด
65803๊ฐ์ block์ด write๋์ด์ผ ํ๋ฉฐ, ์ค๊ฐ๋ง๋ค ์งํ ์ํฉ์ ์ถ๋ ฅํ๋ค. - write ์๋ฃ ํ file์
closeํ๊ณO_RDONLYflag๋ก ๋ค์openํ์ฌread๋ฅผ ์ํํ๊ณ , ์ ์ฅ๋ ๊ฐ์ด block ๋ฒํธ์ ์ผ์นํ๋์ง ๊ฒ์ฆํ๋ค. - ๋ชจ๋ ๊ฒ์ฌ๊ฐ ํต๊ณผ๋๋ฉด ์ฑ๊ณต ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๊ณ
exitํ๋ค.
์ด ํ ์คํธ๋ doubly-indirect block ๊ธฐ๋ฅ์ด ์ ์ฉ๋ file system์ด ์ค์ ๋ก large file์ ์์ ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋์ง๋ฅผ ํ์ธํ๋ ๋ฐ ๋ชฉ์ ์ด ์๋ค.
fd = open("big.file", O_CREATE | O_WRONLY);
...
while(1){
*(int*)buf = blocks;
int cc = write(fd, buf, sizeof(buf));
if(cc <= 0)
break;
blocks++;
}
2.3. /user/symlinktest.c
์ด ํ๋ก๊ทธ๋จ์ symbolic link ๊ธฐ๋ฅ์ ๊ฒ์ฆํ๋ user space ํ ์คํธ ํ๋ก๊ทธ๋จ์ด๋ค.
์ฃผ์ ํ ์คํธ ๊ตฌ์ฑ์ ๋ค์๊ณผ ๊ฐ๋ค.
testsymlinktestsymlink๋๋ ํฐ๋ฆฌ๋ฅผ ๋ง๋ค๊ณ , file์ ํ๋ ์์ฑํ๋ค. ๊ทธ๋ฆฌ๊ณsymlinksystem call์ ์ด์ฉํด ํด๋น file์ target์ผ๋ก ํ๋ link file์ ์์ฑํ๋ค. ์ดํread๋์์ด ์ ์์ ์ผ๋ก ์๋ํ๋์ง ํ์ธํ๊ณ , link๊ฐT_SYMLINKํ์ ์ผ๋ก ์ธ์๋๋์ง๋stat์ผ๋ก ๊ฒ์ฆํ๋ค.
์ดํ ์๋ณธ file์unlinkํ ๋ค broken link์ ์ ๊ทผ์ด ์ฐจ๋จ๋๋์ง, ๋ค์ ์ํ loop ๊ตฌ์กฐ๋ฅผ ๊ฐ๋ link๋ฅผ ๋ง๋ค์์ ๋open์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋์ง ํ์ธํ๋ค.
๋ง์ง๋ง์ผ๋ก ์ฌ๋ฌ ๋จ๊ณ์ link chain์ ์์ฑํ ๋ค, chain์ ์์์ ์ ํตํด ๋ file์ ์ ๊ทผ ๊ฐ๋ฅํ์ง๋ ๊ฒ์ฌํ๋ค.concur
์ฌ๋ฌ process๊ฐ ๋์์symlink๋ฅผ ์์ฑํ๊ณunlink๋ฅผ ๋ฐ๋ณตํ๋ ์ํฉ์ ๋ง๋ค์ด race condition์ด๋ consistency ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๋์ง ๊ฒ์ฆํ๋ค. ๊ฐ process๋ ๋์ผํtarget์ ๋ํด ๋ฐ๋ณต์ ์ผ๋กsymlink/unlink๋ฅผ ์ํํ๋ค.
r = symlink("/testsymlink/a", "/testsymlink/b");
...
if(st.type != T_SYMLINK)
fail("not a symlink");
์ด test๋ symbolic link์ ์์ฑ, ์ญ์ , ๊ฒฝ๋ก ํด์, cycle ์ฒ๋ฆฌ, broken link ๋ฐ ๋์ ์ ๊ทผ ๋ฌธ์ ๋ฅผ ์ข ํฉ์ ์ผ๋ก ๊ฒ์ฆํ๊ฒ ๋๋ค.
3. Implementation
3.1. Copy-on-Write fork ๊ตฌํ
copy-on-write(COW)๋ parent์ child๊ฐ ๊ฐ์ physical page๋ฅผ ๊ณต์ ํ๋ค๊ฐ ์ฒ์์ผ๋ก write๋ฅผ ์๋ํ๋ ์๊ฐ์๋ง page๋ฅผ ๋ณต์ฌํ์ฌ ๋ถ๋ฆฌํ๋ ๋ฐฉ์์ด๋ค. ๊ตฌํ์ ์ธ ๊ฐ์ง ๋ถ๋ถ์ผ๋ก ๊ตฌ์ฑ๋๋ค.
- fork ์ ๊ณต์ page ์ค์
- user mode write fault ์ฒ๋ฆฌ
- physical page reference count ๊ด๋ฆฌ
3.1.1. uvmcopy์์ ํ์ด์ง ๊ณต์ ์ค์
uvmcopy๋ parent์ pagetable์ ์ํํ๋ฉด์ writable page๋ฅผ read-only๋ก ๋ฐ๊พธ๊ณ PTE_COW flag๋ฅผ ์ถ๊ฐํ๋ค. ์ดํ ๋์ผํ physical page๋ฅผ child pagetable์ ๋งคํํ๊ณ , ref count๋ฅผ ์ฆ๊ฐ์ํจ๋ค.
// kernel/vm.c
if(*pte & PTE_W){
*pte &= ~PTE_W; // read-only
*pte |= PTE_COW; // cow ํ์
if(mappages(new, i, PGSIZE, pa, PTE_FLAGS(*pte)) != 0){
*pte |= PTE_W; // ์คํจ ์ ๋ณต๊ตฌ
*pte &= ~PTE_COW;
goto err;
}
} else {
mappages(new, i, PGSIZE, pa, PTE_FLAGS(*pte));
}
inc_ref((void*)pa); // ๊ณต์ page ref count++ ํ๊ฒ ๋จ..
์ด ๊ณผ์ ์ผ๋ก parent์ child๋ ๊ฐ์ physical page๋ฅผ read-only๋ก ๋ฐ๋ผ๋ณด๋ฉฐ, write ์ fault๊ฐ ๋ฐ์ํ๋๋ก ์ค๋น๋๋ค.
3.1.2. usertrap์์ write fault ์ฒ๋ฆฌ
user mode์์ write fault๊ฐ ๋ฐ์ํ๋ฉด usertrap์ด COW ์ฌ๋ถ๋ฅผ ํ์ ํ๋ค. ํด๋น page๊ฐ PTE_COW์ด๊ณ ๊ณต์ ref count๊ฐ 2 ์ด์์ด๋ฉด ์ page๋ฅผ ํ ๋นํด ๋ด์ฉ์ ๋ณต์ฌํ ๋ค child ์ชฝ์ผ๋ก๋ง writable ๋งคํ์ ๋ง๋ ๋ค. ref count๊ฐ 1์ด๋ฉด ๊ฐ์ page๋ฅผ ๊ทธ๋๋ก writable๋ก ์ ํํ๋ค.
// kernel/trap.c
if(r_scause() == 15){ // store fault
uint64 va = r_stval();
pte_t *pte = walk(p->pagetable, va, 0);
if((*pte & PTE_COW) && (*pte & PTE_V) && (*pte & PTE_U)){
uint64 pa = PTE2PA(*pte);
if(get_ref((void*)pa) > 1){ // ๊ณต์ page
char *mem = kalloc();
memmove(mem, (char*)pa, PGSIZE);
uvmunmap(p->pagetable, PGROUNDDOWN(va), 1, 1);
mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE,
(uint64)mem, PTE_R|PTE_W|PTE_X|PTE_U);
} else { // ๋จ๋
page
*pte |= PTE_W;
*pte &= ~PTE_COW;
}
goto trapret;
}
}
์ด logic ๋๋ถ์ write๋ฅผ ์๋ํ process๋ง ์ page๋ฅผ ๊ฐ๊ฒ ๋๊ณ , ๋๋จธ์ง ํ๋ก์ธ์ค๋ ์๋ณธ page๋ฅผ ๊ทธ๋๋ก ์ ์งํ๋ค.
3.1.3. kalloc / kfree์์ reference count ๊ด๋ฆฌ
physical page๋ณ ์ฌ์ฉ ๊ฐ์๋ฅผ ref_counts[] ๋ฐฐ์ด๋ก ๊ด๋ฆฌํ๋ค. ์ page๋ฅผ ํ ๋นํ๋ฉด ref count๋ฅผ 1๋ก ์ค์ ํ๊ณ , page๋ฅผ ๊ณต์ ํ ๋ inc_ref๋ก ์ฆ๊ฐ์ํจ๋ค. kfree๋ ref count๊ฐ 0์ด ๋ ๋๋ง ์ค์ free list์ ๋ฐํํ๋ค.
// kalloc.c
void inc_ref(void *pa){
ref_counts[(uint64)pa / PGSIZE]++;
}
void kfree(void *pa){
uint *ref = &ref_counts[(uint64)pa / PGSIZE];
*ref -= 1;
if(*ref == 0){
memset(pa, 1, PGSIZE);
((struct run*)pa)->next = kmem.freelist;
kmem.freelist = (struct run*)pa;
}
}
์ด๊ธฐํ ์ ๋ชจ๋ usable page์ ref count๋ฅผ 1๋ก ์ค์ ํ ๋ค freerange์์ kfree๋ฅผ ํธ์ถํด free list์ ๋ฃ๋๋ค.
3.1.4. ์ฐ๋ ๊ฒฐ๊ณผ
cowtest์simpletest,threetest,filetest๊ฐ ๋ชจ๋pass๋๋ค.- parent์ child์ write ๋์์ด ์๋ก ์ํฅ์ ์ฃผ์ง ์์ผ๋ฉฐ, ์ฌ์ ํ read-only page๋ฅผ ๊ณต์ ํจ์ผ๋ก์จ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ํฌ๊ฒ ๊ฐ์ํ๋ค.
- ref count ๊ด๋ฆฌ๋ก page ๋์ ์์ด
exitํ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ ์์ ์ผ๋ก ํ์๋๋ค.
copy-on-write fork ๊ตฌํ์ ์ ์ธ ๋ถ๋ถ์ด ๋ง๋ฌผ๋ ค ๋์ํ๋ฉฐ, xv6์ ๊ธฐ๋ณธ fork ๋๋น ์ฑ๋ฅ๊ณผ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ํฌ๊ฒ ํฅ์์ํจ๋ค.
3.2. Large file ์ง์ ๊ตฌํ
large file์ ์ ์ฅํ๊ธฐ ์ํด inode์ block address ๊ตฌ์กฐ๋ฅผ direct + single indirect + double indirect 3๋จ๊ณ๋ก ํ์ฅํ๋ค. bigfile ํ
์คํธ๊ฐ ์๊ตฌํ๋ 65803๊ฐ block์ ์ ํํ ์ง์ํ๋๋ก ์์๋ฅผ ์ฌ์กฐ์ ํ๊ณ , bmap์ itrunc๋ฅผ ์์ ํด block ํ ๋นยทํด์ ๋ฅผ ์ฒ๋ฆฌํ๋ค.
3.2.1. ํต์ฌ ๋งคํฌ๋ก ๋ณ๊ฒฝ
// kernel/param.h
#define FSSIZE 262656 // file system ์ ์ฒด block ์
// kernel/fs.h
#define NDIRECT 11 // direct pointer 11๊ฐ
#define NINDIRECT (BSIZE / sizeof(uint)) // 256
#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT*NINDIRECT) // 11 + 256 + 65536
- direct pointer๋ฅผ 11๊ฐ๋ก ์ค์ฌ
addrs[NDIRECT+2]๋ฐฐ์ด์ ๋ง์ง๋ง ๋ ์นธ์ single indirect, double indirect pointer๋ก ์ฌ์ฉํ๋ค. MAXFILE์ double indirect ๋ฒ์๊น์ง ๊ณ์ฐํด bigfile ํ ์คํธ์ block ์์ ์ผ์น์ํจ๋ค.
3.2.2. dinode ๊ตฌ์กฐ
struct dinode {
short type;
short major;
short minor;
short nlink;
uint size;
uint addrs[NDIRECT+2]; // [0..10] direct, [11] single, [12] double
};
3.2.3. bmap ์์
double indirect ๋ถ๊ธฐ๋ฅผ ์ถ๊ฐํด ๋ ๋จ๊ณ๋ก block์ ํ ๋นยทํ์ํ๋ค.
// kernel/fs.c (๋ฐ์ท)
bn -= NINDIRECT;
// double indirect
if(bn < NINDIRECT * NINDIRECT){
// 1๋จ๊ณ: double indirect pointer๊ฐ ์์ผ๋ฉด ํ ๋น
if((addr = ip->addrs[NDIRECT+1]) == 0){
addr = balloc(ip->dev);
ip->addrs[NDIRECT+1] = addr;
}
// 2๋จ๊ณ: first level index ๊ณ์ฐ
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if((addr = a[bn / NINDIRECT]) == 0){
addr = balloc(ip->dev);
a[bn / NINDIRECT] = addr;
log_write(bp);
}
brelse(bp);
// 3๋จ๊ณ: second level index
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if((addr = a[bn % NINDIRECT]) == 0){
addr = balloc(ip->dev);
a[bn % NINDIRECT] = addr;
log_write(bp);
}
brelse(bp);
return addr;
}
๋์ ์์
- double indirect pointer๊ฐ ์์ผ๋ฉด ์ block์ ํ ๋นํ๋ค.
- ์ฒซ ๋ฒ์งธ ๋ ๋ฒจ ๋ฐฐ์ด์ ๊ฐ์ ธ์ index๋ฅผ ๊ณ์ฐํ๊ณ , ๋ ๋ฒ์งธ ๋ ๋ฒจ ๋ฐฐ์ด block์ ํ ๋นโง๊ธฐ๋กํ๋ค.
- ๋ ๋ฒ์งธ ๋ ๋ฒจ ๋ฐฐ์ด์์ ์ค์ data block์ ํ ๋นํด ๋ฐํํ๋ค.
3.2.4. itrunc ๊ฐ๋
์์ฝ
itrunc๋ direct, single indirect, double indirect ๋ธ๋ก์ ๋ชจ๋ ์ํํ๋ฉฐ bfree๋ฅผ ํธ์ถํด ํด์ ํ๋ค.
double indirect๋ ๋ ๋จ๊ณ์ ๊ฑธ์ณ bread โ loop โ bfree ์์ผ๋ก ์ฌ๊ท์ ์ผ๋ก ์ฒ๋ฆฌํ๋ค.
3.2.5. bigfile ํ
์คํธ์์ ์ ํฉ์ฑ
bigfile.c๋MAXFILE์ ํด๋นํ๋65803block์ ์์ฐจ writeํ ๋ค read๋ก ๊ฒ์ฆํ๋ค.- double indirect ๋ถ๊ธฐ์ ์์ ์กฐ์ ์ผ๋ก write loop๊ฐ block ํ ๋น ์ค๋ฅ ์์ด ์๋ฃ๋๋ค.
- read ๋จ๊ณ์์ block ๋ฒํธ์ data๊ฐ ์ผ์นํด ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ด ๋ณด์ฅ๋๋ค.
์ด๋ก์จ xv6 file system์ ์ฝ 66 MB(65803 * 1024) ๊ท๋ชจ์ ํ์ผ๊น์ง ์์ ์ ์ผ๋ก ์ ์ฅยท์ฝ๊ธฐํ ์ ์๋ค.
3.3. Symbolic link ๊ตฌํ
symbolic link์ ํ์ผ ์ด๋ฆ ๋์ ๊ฒฝ๋ก ๋ฌธ์์ด์ ์ ์ฅํด ๋ค๋ฅธ ํ์ผ์ ๊ฐ์ ์ฐธ์กฐํ๋ inode์ด๋ค.
๊ตฌํ์ inode type ์ถ๊ฐ, sys_symlink ์์คํ
์ฝ ์ ์, sys_open์ ๊ฒฝ๋ก ํด์ ํ์ฅ, ๊ทธ๋ฆฌ๊ณ ๋ฃจํ ๋ฐฉ์ง๋ฅผ ์ํ ๊น์ด ์ ํ์ผ๋ก ๊ตฌ์ฑ๋๋ค.
3.3.1. ์ฃผ์ ์์ ๋ฐ ํ์
T_SYMLINKํ์ผ ํ์ ์ inode์ stat ๊ตฌ์กฐ์ ์ถ๊ฐMAX_SYMLINK_LOOPS์์๋ฅผ10์ผ๋ก ์ ์ํด ๋ฌดํ ์ํ์ ๋ฐฉ์งstat,user.h, ์์คํ ์ฝ ๋ฒํธ ํ ์ด๋ธ์ ํ์ โงํจ์โง๋ฒํธ๋ฅผ ๋ชจ๋ ๋ฐ์
#define T_SYMLINK 4 // kernel/fs.h, kernel/stat.h
#define MAX_SYMLINK_LOOPS 10
#define SYS_symlink 22 // syscall ๋ฒํธ
3.3.2. sys_symlink
sys_symlink(const char *target, const char *linkpath)๋ ๋ค์ ์์๋ก ๋์ํ๋ค.
create(linkpath, T_SYMLINK, 0, 0)ํธ์ถ๋ก ๋น symlink inode ์์ฑwritei๋ก target ๊ฒฝ๋ก ๋ฌธ์์ด์ inode ๋ฐ์ดํฐ ์์ญ์ ๊ธฐ๋ก- ์์
์คํจ ์
iunlockputํ-1๋ฐํ
if((ip = create(path, T_SYMLINK, 0, 0)) == 0)
return -1;
if(writei(ip, 0, (uint64)target, 0, strlen(target)) < strlen(target))
return -1;
3.3.3. ys_open์ ๊ฒฝ๋ก ํด์
open ์ symlink๋ฅผ ์๋์ผ๋ก ๋ฐ๋ผ๊ฐ๋๋ก while ๋ฃจํ๋ฅผ ์ถ๊ฐํ๋ค.
- ์ต๋
MAX_SYMLINK_DEPTH๋งํผ ๋ฐ๋ณตํ๋ฉฐnamei๋ก ํ์ฌ ๊ฒฝ๋ก๋ฅผ lookup - inode๊ฐ
T_SYMLINK,O_NOFOLLOW๊ฐ ์๋ ๊ฒฝ์ฐ- symlink ํ์ผ์์ target ๊ฒฝ๋ก ๋ฌธ์์ด์ ์ฝ์ด
path๋ณ์์ ๋ณต์ฌ - ๊น์ด ์นด์ดํธ๋ฅผ ์ฆ๊ฐ์ํค๊ณ ๋ฃจํ ์ฌ์์
- symlink ํ์ผ์์ target ๊ฒฝ๋ก ๋ฌธ์์ด์ ์ฝ์ด
- depth ์ด๊ณผ ์ ์คํจ
for(int depth = 0; depth < MAX_SYMLINK_DEPTH; depth++){
if((ip = namei(path)) == 0){
if(omode & O_CREATE)
ip = create(path, T_FILE, 0, 0);
else
return -1;
} else
ilock(ip);
if(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){
char link_target[MAXPATH];
int len = readi(ip, 0, (uint64)link_target, 0, MAXPATH - 1);
iunlockput(ip);
link_target[len] = '\0';
safestrcpy(path, link_target, MAXPATH);
continue; // ๋ค์ ํด์
}
goto found; // ์ค์ ํ์ผ ๋์ฐฉ
}
3.3.4. create ํจ์ ํธํ์ฑ
create๋ ๊ธฐ์กด T_FILEโยทโT_DIRยทT_DEVICE ๋ก์ง์ ์ ์งํ๋ฉฐ, T_SYMLINK๋ฅผ ์ถ๊ฐ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์๋ค. symlink ์์ฑ ์ ๋ณ๋ ์ด๊ธฐํ๋ ํ์ ์๊ณ , nlink๋ 1๋ก ์ค์ ํ ๋ค dirlink๋ก ๋๋ ํ ๋ฆฌ์ ๋ฑ๋กํ๋ค.
3.3.5. ์ ์ฒด์ ์ธ ๋์ ๊ณผ์
symlink target link๋ช ๋ น์ด ๋ค์ด์ค๋ฉดsys_symlink๊ฐ link ํ์ผ์ ๋ง๋ค๊ณ target ๊ฒฝ๋ก๋ฅผ ๋ด์ฉ์ผ๋ก ๊ธฐ๋กํ๋ค.- ์ดํ
open(link)ํธ์ถ์ ๋ฃจํ๋ฅผ ๋๋ฉฐ symlink ํ์ผ์ ์ด๊ณ target ๋ฌธ์์ด์ ์ฝ์ด ๋ค์namei๋ฅผ ์ํํ๊ฒ ๋๋ค. - ์ต๋ 10๋จ๊ณ๊น์ง link chain์ ๋ฐ๋ผ๊ฐ๋ฉฐ, cycle์ด๊ฑฐ๋ ๊น์ด ์ด๊ณผ ์ ์ค๋ฅ๋ฅผ ๋ฐํํ๋ค.
O_NOFOLLOWflag๋ฅผ ์ฌ์ฉํ๋ฉดsymlink์์ฒด๋ฅผ ์ด ์ ์๋ค.
์ด ๋ฐฉ์์ผ๋ก symlinktest์ link ์์ฑ, loop ๊ฐ์ง, broken link ์ฒ๋ฆฌ, ๋์์ฑ(createโยทโunlink race) ์๋๋ฆฌ์ค๊ฐ ๋ชจ๋ ํต๊ณผ๋๋ค.
4. Results
4.1. cowtest.c
4.1.1. Sub-test comparison
| Sub-test | Expected reference | Actual output | Analysis & reasoning | Verdict |
|---|---|---|---|---|
simpletest | simple: ok | identical | COW page๋ ๋ณต์ฌ ์์ด ๊ณต์ , ref count +1/-1 ์ ์ | pass |
threetest (ร3) | ์ธ ์ค ๋ชจ๋ three: ok | identical | ์ธ ํ๋ก์ธ์ค๊ฐ ๋์์ write โ ๊ณต์ page ๋ณต์ฌ ํ free, ref count leak ์์ | pass |
filetest | file: ok ๋ฐ ๋ง์ง๋ง ALL COW TESTS PASSED | identical | copyout๊ฐ COW page๋ฅผ ์ง์ ์ฒ๋ฆฌํ๋๋ก ์์ ํ์ฌ pipe alloc ์ฑ๊ณต | pass |
โ ๋ฉ๋ชจ๋ฆฌ ๊ณต์ ยท๋ณต์ฌยทํด์ ์ ๊ณผ์ ์ด ๋ชจ๋ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋ค.
4.2. bigfile.c
| ํญ๋ชฉ | Expected | Actual | ๋ถ์ |
|---|---|---|---|
| Blocks written | 65 803 | 65 803 | double-indirect bmap๊ฐ ์ ์ฒด ํ ๋น ๋ฒ์ ์ฒ๋ฆฌ |
| Verification read | all match | all match | block ๋ฒํธ์ ๋ฐ์ดํฐ ์ผ์น, ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ ํต๊ณผ |
| Runtime (QEMU) | ์ ๋ถ | ์ฝ 20 ๋ถ | ๋๊ธฐ์ log I/O ์ง์ฐ ํ์ฉ ๋ฒ์ |
โ 66 MB๊ธ file์ ์ค๋ฅ ์์ด write-read ํ์ผ๋ฏ๋ก large-file ํ์ฅ์ด ์ฑ๊ณต์ ์ผ๋ก ๋์ํ๋ค๊ณ ํ ์ ์๋ค.
4.3. symlinktest.c
| Sub-test | Observation | Explanation | Verdict |
|---|---|---|---|
Basic link (bโa) | b ํ์
T_SYMLINK, ์ฝ์ผ๋ฉด 'a' | create๊ฐ T_SYMLINK ์ ๋ฌ, target ๊ฒฝ๋ก ์ ์ฅ | pass |
| Broken link | ์๋ณธ a ์ญ์ ํ open(b) ์คํจ | sys_open์ด depth loop ๋ด์์ inode ๋ฏธ์กด์ฌ ์ ์๋ฌ ๋ฐํ | pass |
| Cyclic link (aโb) | open(b) ์ค๋ฅ ๋ฐํ | depth ์นด์ดํฐ 10 ๋๋ฌ ํ ๋ฃจํ ๊ฐ์ง | pass |
| Chain (1โ2โ3โ4) | write via 1, read via 4 ๋์ผ ๋ฌธ์ | 4-step ํด์์ด MAX_SYMLINK_LOOPS-์์ ์๋ฃ | pass |
| Concurrency | ๋ child ๋ชจ๋ ์ ์ ์ข ๋ฃ | inode lock + log write ๋ก race condition ๋ฐฉ์ง | pass |
โ ๋งํฌ ์์ฑยท์ญ์ ยทํด์ยท๋์์ฑ์ด ๋ชจ๋ ์๊ตฌ ์กฐ๊ฑด์ ์ถฉ์กฑํ๋ค.
4.4. Completion process
make qemu- QEMU shell:
xv6 kernel is booting
init: starting sh
$ cowtest
simple: ok
simple: ok
three: ok
three: ok
three: ok
file: ok
ALL COW TESTS PASSED
$ bigfile
..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
wrote 65803 blocks
bigfile done; ok
$ symlinktest
Start: test symlinks
test symlinks: ok
Start: test concurrent symlinks
test concurrent symlinks: ok
$
- ์ธ test ๋ชจ๋
โฆ ok/PASSED์ถ๋ ฅ๋์์.

4.5. Program-flow snapshot ๊ธฐ๋ฅ๋ณ ๋์ ํ๋ฆ ์์ฝ
- COW :
fork โ uvmcopy(PTE_COW) โ write fault โ usertrap โ kalloc+copy โ resume - Large file :
write loop โ bmap(directโsingleโdouble) โ log_write โ disk - Symlink :
sys_open โ namei โ ilock โ type check โ readi(target) โ namei retry (โค10)
5. Troubleshooting
5.1. Copy-on-Write troubleshooting
5.1.1. Initial symptom
simpletest, threetest๋ ํต๊ณผํ์ผ๋ filetest์์ file: pipe() failed ๋ฉ์์ง ๋ฐ์.
5.1.2. Hypotheses
- Memory leak :
pipealloc๋ด๋ถkalloc์คํจ ๊ฐ๋ฅ์ฑ โ ๋๋ฒ๊ทธ ๋ก๊ทธ ์ถ๊ฐ, ๋ฏธ์ถ๋ ฅ. - File structure exhaustion :
filealloc์คํจ ๊ฐ๋ฅ์ฑ โ ๋๋ฒ๊ทธ ๋ก๊ทธ ์ถ๊ฐ, ๋ฏธ์ถ๋ ฅ.
5.1.3. Root cause
pipe๋ fd ๋ฒํธ๋ฅผ user ๋ฐฐ์ด์ ๋ณต์ฌํ๊ธฐ ์ํดcopyoutํธ์ถ.- parent page๋
PTE_COW+ read-only. - ๊ธฐ์กด
copyout์PTE_W๊ฐ ๊บผ์ง ํ์ด์ง๋ฅผ ๋ง๋๋ฉด ์ฆ์-1๋ฐํ โpipealloc์คํจ.
// old copyout (excerpt)
if((*pte & PTE_W) == 0)
return -1; // write-protect โ ์ฆ์ ์คํจ
5.1.4. Fix
copyout์ด PTE_COW ํ๋๊ทธ๋ฅผ ๊ฐ์งํ๋ฉด ์ง์ COW page ๋ณต์ฌ ํ ์ฌ์๋.
// kernel/vm.c (core patch)
if((*pte & PTE_COW) && !(*pte & PTE_W)){
if(copy_cow_page(pagetable, va) < 0)
return -1;
}
5.1.5. Result
pipe ์ฑ๊ณต โ filetest ok โ ์ต์ข
ALL COW TESTS PASSED.
5.2. Large-file troubleshooting
5.2.1. Symptom 1
wrote 268 blocks ํ file is too small ์ค๋ฅ.
5.2.2. Cause 1
NDIRECT๊ฐ 12๋ก ๋จ์ double-indirect ํฌ์ธํฐ ๋ฏธ์ฌ์ฉ.
5.2.3. Fix 1
NDIRECT โ 11, addrs[NDIRECT+2]๋ก ๊ตฌ์กฐ์ฒด ๊ฐฑ์ โ double-indirect ํ์ฑํ.
5.2.4. Symptom 2
mkfs assertion (BSIZE % sizeof(struct dinode)) == 0 ์คํจ.
5.2.5. Cause 2
struct dinode ํฌ๊ธฐ๊ฐ 64 B ๋ฐฐ์ ์๋.
5.2.6. Fix 2
๋ฐฐ์ด ํฌ๊ธฐ ์กฐ์ ์ผ๋ก 64 B ์ ๋ ฌ ํ๋ณด โ ์ด๋ฏธ์ง ์์ฑ ์ฑ๊ณต.
5.2.7. Symptom 3
bigfile ์คํ ์๊ฐ์ด ๊ณผ๋(QEMU + sync log).
5.2.8. Work-around
๋๊ธฐ ํ wrote 65803 blocks / bigfile done; ok ์ถ๋ ฅ.
5.3. Symlink troubleshooting
5.3.1. Initial compile errors
๋ฏธ์ ์ ์์ยท์ค๋ณต statยท๋ฏธ๋ฑ๋ก syscall ๋ฑ.
5.3.2. Fix steps
์์ ์ ์, ํจ์ ์ํ ์ถ๊ฐ, syscall.h / syscall.c / usys.pl ๊ฐฑ์ ,ulib.c ์ค๋ณต stat ์ ๊ฑฐ ๋ฑ์ผ๋ก ์ปดํ์ผ ์๋ฃ.
5.3.3. Runtime error A โ FAILURE: b isn't a symlink
- Cause :
create()๊ฐT_SYMLINK๋ฅผ ๋ฌด์ํ๊ณT_FILE๋ก ํ ๋น. - Fix :
ialloc(dev, type)๋ก ํ์ ๊ทธ๋๋ก ์ ๋ฌ.
5.3.4. Runtime error B โ broken link panic
- Cause :
sys_open์ด depth /O_NOFOLLOW์ฒดํฌ ์์ด ์ฌํด์. - Fix : ๊น์ด ์นด์ดํฐ(โค10) ๋ฐ
O_NOFOLLOW๋ถ๊ธฐ ์ถ๊ฐ.
5.3.5. Runtime error C โ concurrent unlink panic
- Cause :
sys_symlink๊ฐ write ์ค lock ํด์ โ race. - Fix :
writei์๋ฃ ํiunlockput; ๊ฐ ๋จ๊ณlog_writeํธ์ถ.
5.3.6. Result
symlinktest์ basicยทbrokenยทloopยทconcurrent ์๋๋ฆฌ์ค ๋ชจ๋ ok ์ถ๋ ฅ.
6. Additional Content
6.1. Reference notes for COW / Large file / Symbolic link implementation in xv6-riscv
- RISC-V์๋ CR0(Write Protect) ๋นํธ๊ฐ ์์ผ๋ฏ๋ก, ํ์ด์ง๋ฅผ read-only๋ก ๋ง๋๋ ์์ ์ PTE_W ํด๋ฆฌ์ด + TLB flush๋ก๋ง ์๊ฒฐ๋๋ค.
- double-indirect block ๊ตฌํ ์
balloc์ผ๋ก ํ ๋น๋ฐ์ ๋ ๋ฒ์งธ ๋ ๋ฒจ ๋ธ๋ก์ ๋ฐ๋์log_writeํbrelseํด์ผ log ์ผ๊ด์ฑ์ด ์ ์ง๋๋ค. - symlink ํด์์
namei์ ๋์ผํ ์ ๊ธ ์์๋ฅผ ๋ฐ๋ผ์ผ deadlock์ ํผํ ์ ์๋ค. ํนํsys_open๋ฃจํ ๋ด๋ถ์์ilock(ip)ํreadi๋ฅผ ํธ์ถํ ๋ log transaction์ด ์ด๋ฆฐ ์ํ์ฌ์ผ ํ๋ค.

