기존 fork
시스템 호출은 부모 프로세스의 메모리 공간을 자식 프로세스에 즉시 복사합니다. 이는 복사해야 할 공간의 크기가 클 경우, 많은 시간이 소요됩니다.
복사를 지연시켜, 복사가 되는 횟수를 줄여야 합니다. 대개 fork
호출 뒤에는 exec
이 호출되며, 자식 프로세스는 프로그램을 적재하기 위해 새로운 페이지가 할당되므로 부모의 페이지가 필요없습니다. 따라서, copy-on-write 전략을 사용해 fork
호출 시에는 복사가 일어나지 않고, 자식 프로세스에서 최초로 write를 하는 경우에 복사를 하는 지연 복사를 하면 됩니다.
implement copy-on-write fork · plming/xv6-labs-2022@7fad4ac
fork
호출 시, 부모와 자식의 PTE(page table entry)에서 write bit를 unset합니다. 따라서 write 시도 시 trap handler가 실행되며, 이때 부모/자식 누가 쓰기를 하든 복사를 하면 됩니다.
이때, 기존 구현의 read-only page 기능을 고려하여, PTE의 RSW 비트를 활용하여 PTE_C
비트를 새로 정의합니다. 이 비트는 fork
호출 시, 원래 writeable한 페이지인지를 나타냅니다. 따라서 PTE_W
와 PTE_C
가 모두 꺼진 비트는 read-only 페이지입니다.
PTE_R | PTE_W | PTE_C | 설명 |
---|---|---|---|
1 | 0 | 0 | 읽기 전용 페이지. 쓰기 시 fault 발생, 프로세스 종료 |
1 | 0 | 1 | COW 공유 상태. 아직 복사되지 않은 페이지 |
1 | 1 | 0 | 일반 페이지. COW가 아닌 정상 쓰기 가능 상태 |
참조 카운팅(reference counting) 전략을 사용합니다.
모든 물리 페이지는 커널의 kalloc
, kfree
함수를 통해 할당 및 해제되며, 커널 공간 내 페이지 단위의 참조 횟수를 저장하는 배열을 두어 관리합니다.
kalloc
호출 시에는 해당 페이지의 참조 카운트를 1로 초기화하고, fork
호출 시 공유된 페이지의 참조 카운트를 1 증가시킵니다.
kfree
호출 시에는 참조 카운트를 1 감소시키고, 0이 될 경우에만 메모리를 해제합니다. 이때 배열의 인덱스는 물리 주소를 페이지 크기로 나눈 값을 사용해, 인덱스 변환을 효과적으로 처리했습니다.