모든 태그

# QEMU

11개의 글

유저 스레드: 커널 없이 갈아타기

스레드는 꼭 커널이 만들어줘야 할까. 한 프로세스 안에서, 커널의 도움 없이 유저 공간만으로 실행 흐름을 갈아타는 협조적 유저 스레드를 만든다. 이건 이 시리즈에서 한 번 실패했다 되돌렸던 주제다. 진짜 원인인 단일 페이지 모델의 전역 변수 제약, 그리고 힙 스택 위에 트랩 프레임을 저장하다 나는 커널 페이지 폴트를 찾아내고, 커널을 전혀 바꾸지 않고 두 가지 우회로 해결한다.

TCP: 신뢰성은 번호 매기기에서 온다

UDP는 패킷을 던지고 잊는다. TCP는 "내가 어디까지 보냈고 어디까지 받았나"를 시퀀스 번호로 추적해, 순서와 도착을 보장한다. net 스택 위에 TCP 세그먼트와 3-way 핸드셰이크를 올려, 수동 개방 서버를 만든다. 호스트가 접속해 보낸 데이터를 게스트 커널이 받아 에코하고, 그 왕복으로 핸드셰이크부터 종료까지를 검증한다. 그리고 우리가 일부러 빼놓은 재전송, 혼잡 제어, 윈도우의 트레이드오프까지 다룬다.

저널링 파일시스템: 절반만 쓰이지 않게

쓰기 도중 전원이 나가면 파일시스템은 어떻게 될까. 데이터는 썼는데 디렉터리는 못 썼다면 디스크가 깨진다. 저널링(write-ahead log)은 이걸 막는다. 변경을 제자리에 바로 쓰지 않고 먼저 로그에 모은 뒤, 커밋 표시를 찍고, 그제서야 설치한다. 크래시가 나도 "전부 반영" 또는 "전혀 반영 안 됨" 둘 중 하나라 디스크는 항상 일관적이다. 여기에 삭제(rm)와 빈 블록 재사용까지 더해 읽기 전용이던 FS를 진짜 읽기-쓰기로 키운다.

Copy-on-Write fork: 복사를 미루는 기술

fork는 주소공간을 통째로 복사한다. 하지만 대부분의 자식은 곧장 exec해 그 복사본을 버린다. Copy-on-Write는 이 낭비를 없앤다. fork 때는 페이지를 복사하지 않고 부모와 읽기 전용으로 공유하다가, 누군가 쓰는 "그 순간"에만 복제한다. 물리 페이지 참조 카운트와 쓰기 페이지 폴트로 이걸 구현하고, 우리 커널만의 설계 제약(스택은 공유 못 한다)과 그 트레이드오프까지 짚는다.

멀티코어로 가다: SMP와 락

단일 코어 커널을 여러 코어가 함께 도는 SMP 커널로 키운다. SBI HSM으로 보조 코어를 깨워 부팅하고, 스핀락으로 공유 자료구조를 보호하고, 컨텍스트 스위치를 가로지르며 락을 넘기는 baton 방식으로 멀티코어 스케줄러를 만든다. 그 과정에서 만난 가장 고약한 버그, 곧 유저가 tp 레지스터를 덮어 코어 번호가 틀어지던 문제와 그 해결까지 다룬다.

demand paging, mmap, 그리고 쓰기 가능 파일시스템

작은 유닉스 위에 메모리 관리를 깊이 올린다. 예외 핸들러를 "출력만 하던 것"에서 "실제로 페이지를 만드는 것"으로 키워, sbrk 기반 지연 할당(demand paging)과 파일을 메모리처럼 읽는 mmap을 구현한다. 그리고 읽기 전용이던 파일시스템을 블록 할당과 디스크 쓰기로 쓰기 가능하게 만들어, 파일이 재부팅에도 살아남게 한다. 페이지 폴트가 RISC-V에서 어떻게 S-mode로 전달되는지(medeleg)부터 차근히.