운영체제 구현 — xv6로 배우는 미니 OS 만들기
xv6는 MIT에서 교육 목적으로 만든 미니 운영체제입니다. Unix V6를 현대적으로 재구현한 것으로, 약 1만 줄의 C 코드로 OS의 핵심 개념을 모두 담고 있습니다.
xv6란
MIT 6.S081(Operating System Engineering) 강의를 위해 만든 교육용 운영체제 입니다.
- Unix V6 (1975년)의 현대적 재구현
- RISC-V 아키텍처 기반 (xv6-riscv)
- 약 1만 줄의 C + 어셈블리 코드
- 멀티프로세서 지원, 가상 메모리, 파일 시스템 포함
xv6의 구조
┌──────────────────────────────────┐
│ 사용자 프로그램 (sh, ls, cat 등) │
├──────────────────────────────────┤
│ 시스템 콜 인터페이스 │
│ (fork, exec, read, write 등) │
├──────────────────────────────────┤
│ 커널 │
│ ┌────────┬──────┬────────────┐ │
│ │ 프로세스 │ 메모리│ 파일 시스템 │ │
│ │ 관리 │ 관리 │ │ │
│ ├────────┼──────┼────────────┤ │
│ │ 트랩/ │ 디바이│ 디스크 │ │
│ │ 인터럽트│ 스 │ 드라이버 │ │
│ └────────┴──────┴────────────┘ │
├──────────────────────────────────┤
│ 하드웨어 (RISC-V, QEMU 에뮬레이션) │
└──────────────────────────────────┘
핵심 구현 살펴보기
1. 프로세스 관리
// kernel/proc.h
struct proc {
struct spinlock lock;
enum procstate state; // UNUSED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE
int pid;
struct proc *parent;
void *kstack; // 커널 스택
uint64 sz; // 프로세스 메모리 크기
pagetable_t pagetable; // 페이지 테이블
struct trapframe *trapframe; // 트랩 시 레지스터 저장
struct file *ofile[NOFILE]; // 열린 파일
struct inode *cwd; // 현재 디렉토리
char name[16];
};
2. 시스템 콜
// kernel/syscall.c
// 트랩 발생 → syscall() 호출 → 시스템 콜 번호로 분기
void syscall(void) {
int num;
struct proc *p = myproc();
num = p->trapframe->a7; // a7 레지스터에 시스템 콜 번호
if (num > 0 && num < NELEM(syscalls) && syscalls[num]) {
p->trapframe->a0 = syscalls[num](); // 시스템 콜 실행, 결과를 a0에
} else {
printf("unknown sys call %d\n", num);
p->trapframe->a0 = -1;
}
}
3. 스케줄러
// kernel/proc.c
// 라운드 로빈 스케줄러
void scheduler(void) {
struct proc *p;
struct cpu *c = mycpu();
for (;;) {
// 프로세스 테이블을 순회하며 RUNNABLE 프로세스를 찾음
for (p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if (p->state == RUNNABLE) {
p->state = RUNNING;
c->proc = p;
swtch(&c->context, &p->context); // 컨텍스트 스위칭
c->proc = 0;
}
release(&p->lock);
}
}
}
4. 가상 메모리 (페이지 테이블)
xv6는 RISC-V의 Sv39 페이지 테이블 (3단계, 39비트 가상 주소)을 사용합니다.
// kernel/vm.c
// 가상 주소를 물리 주소로 매핑
int mappages(pagetable_t pagetable, uint64 va, uint64 size,
uint64 pa, int perm) {
// 3단계 페이지 테이블 워크
// PTE 설정: 물리 주소 + 권한 비트
}
5. 파일 시스템
xv6 파일 시스템 구조:
┌────────┬────────┬────────┬────────┬───────────────┐
│ Boot │ Super │ Log │ Inode │ Data Blocks │
│ Block │ Block │ Blocks │ Blocks │ │
└────────┴────────┴────────┴────────┴───────────────┘
- 저널링(log) 지원으로 크래시 복구
- inode 기반 파일 관리
- 디렉토리는 특수한 파일 (이름→inode 번호 매핑)
xv6 빌드 및 실행
# 소스 클론
git clone https://github.com/mit-pdos/xv6-riscv.git
cd xv6-riscv
# 빌드 및 QEMU에서 실행
make qemu
# xv6 셸에서
$ ls
$ echo hello
$ cat README
xv6로 배울 수 있는 것
| 개념 | xv6에서의 구현 |
|---|---|
| 프로세스 생성 | fork(), exec() |
| 컨텍스트 스위칭 | swtch() 함수 (어셈블리) |
| 시스템 콜 | 트랩 핸들러 + syscall() |
| 가상 메모리 | 3단계 페이지 테이블 |
| 파일 시스템 | inode, 디렉토리, 저널링 |
| 동기화 | spinlock, sleep/wakeup |
| 인터럽트 | PLIC (외부), CLINT (타이머) |
핵심 포인트
- xv6를 공부하는 이유: 실제 OS의 핵심 개념을 간결한 코드로 이해할 수 있음
- fork()의 내부: 페이지 테이블 복사 + 프로세스 구조체 생성 + RUNNABLE 상태로 설정
- 컨텍스트 스위칭이 실제로 무엇을 저장하는가: callee-saved 레지스터 + 스택 포인터
정리
xv6는 OS의 핵심을 1만 줄로 응축한 교과서적인 운영체제입니다. 프로세스, 메모리, 파일 시스템, 동기화의 실제 구현을 코드로 확인할 수 있어서, OS 개념을 깊이 이해하고 싶은 개발자에게 최고의 학습 자료입니다.