처음부터 직접 운영체제 커널 만들기

17 hours ago 2

  • RISC-V 아키텍처에서 타임셰어링 운영체제 프로토타입 커널을 구현한 경험을 공유
  • 시분할(time-sharing) 커널의 개념과 동작 방식을 실습 중심으로 설명하며, C 대신 Zig로 구현해 재현성을 높임
  • 커널과 사용자 코드를 하나의 바이너리로 묶는 unikernel 접근을 취하고, 콘솔 출력과 타이머 제어를 위해 OpenSBI를 활용하는 레이어링을 채택
  • 스레드는 사용자 모드(U-mode) 에서 실행되고, 커널은 감독자 모드(S-mode) 에서 타이머 인터럽트를 통해 컨텍스트 스위치를 수행하며, 시스템 호출로 경계를 넘나듦
  • 핵심은 인터럽트 프로로그/에필로그가 쌓아둔 스택 프레임을 교체해 다른 스레드의 레지스터 집합과 CSR을 복원함으로써 흐름을 전환하는 기법
  • QEMU 가상 머신과 최신 OpenSBI를 기반으로 누구나 재현 가능한 학습 환경을 제공하며, 스레드·프로세스·컨테이너 등 가상화 스펙트럼을 개념적으로 연결해 교육 및 실습 목적에 적합한 기초 자료로 가치가 있음

개요

  • RISC-V 아키텍처에서 타임셰어링 운영체제 커널을 직접 구현한 과정을 소개함
  • 주요 독자는 시스템 소프트웨어 및 컴퓨터 구조 입문자, 학생, 그리고 저수준 동작 원리 이해에 관심 있는 엔지니어
  • 이 실험은 기존 C 언어 대신 Zig 언어를 사용하여 실습 재현성을 높였으며 설치가 간단함
  • 최종 코드는 popovicu/zig-time-sharing-kernel 저장소에 공개되어 있으며, 본문과 약간의 싱크 차이가 있을 수 있음
    • 글의 코드 발췌보다 저장소 버전을 단일 신뢰원으로 간주할 것을 권장함
    • 실습 시 저장소의 링커 스크립트·빌드 옵션을 기준으로 환경을 맞추는 편의를 제공함

Recommended reading

  • 글은 레지스터, 메모리 주소 지정, 인터럽트 등 컴퓨터 구조 기초를 전제함
    • 선행 자료로 Bare metal on RISC-V, SBI 부팅 과정, 타이머 인터럽트 예제를 추천
    • 마이크로 리눅스 배포 글은 커널·유저 공간 분리 철학 이해에 선택적으로 유용

Unikernel

  • 애플리케이션과 OS 커널을 단일 실행 파일로 링크하는 unikernel 구성을 채택함
    • 런타임의 로더·링커 복잡도를 회피하고, 사용자 코드를 커널과 함께 메모리에 적재하는 단순화 달성
    • 교육 및 재현 목적에서 배포 단순성환경 일관성을 확보하는 장점 제공

SBI layer

  • RISC-V는 M/S/U 모드의 권한 모델을 사용하며, 본 실험은 OpenSBI를 M-모드에 두고 커널을 S-모드에서 동작시킴
    • 콘솔 출력과 타이머 장치 제어를 SBI로 위임해 이식성을 확보
    • SBI 미가용 시 UART MMIO를 폴백으로 사용하되, 실습에선 최신 OpenSBI 사용을 권장

Goal for the kernel

  • 단순화를 위해 정적 스레드만 지원하고, 스레드는 종료하지 않는 함수로 구성
    • 스레드는 U-모드에서 실행되며 S-모드 커널로 시스템 호출을 보냄
    • 타이머 틱마다 다른 스레드로 전환될 수 있도록 단일 코어 기준의 시분할 스케줄링을 구현

Virtualization and what exactly is a thread

  • 시분할 스레딩은 프로그래밍 모델을 변화시키지 않고 단일 코어에서 여러 작업을 병행하는 가상화 방식
    • 협력형 스케줄링과 달리 명시적 yield 없이 타이머 인터럽트로 전환이 발생
    • 스레드는 각자 건드릴 수 없는 레지스터 집합과 스택을 가지며, 나머지 메모리는 공유 가능

The stack and memory virtualization

  • 스레드는 별도 스택을 가져야 하며, 호출 규약상 지역 변수·ra 보존 등 실행 컨텍스트 유지에 필수 요소
    • 가상화 스펙트럼은 스레드 < 프로세스 < 컨테이너 < VM로 이어지며 격리 수준과 시야(view) 가 달라짐
    • 리눅스에서 컨테이너는 chroot, cgroups커널 메커니즘 조합으로 구현됨

Virtualizing a thread

  • 본 실험의 최소 가상화 목표는 프로그래밍 모델 불변, 레지스터·일부 CSR 보호, 개별 스택 할당
    • 레지스터 뷰가 보호되지 않으면 의미 있는 계산이 불가능해지는 이유를 강조
    • 초기 a0 등을 스택에 시딩해 스레드 시작 시 인자 전달을 간결히 처리

Interrupt context

  • 인터럽트는 프로로그/에필로그로 레지스터를 스택에 보존/복원하는 함수 호출 유사 모델로 이해할 수 있음
    • 비동기 타이머 인터럽트가 레지스터를 망가뜨리지 않도록 보존 규약 준수가 필수
    • 예제 어셈블리는 x0–x31 보존에 더해 sstatus, sepc, scause, stvalCSR까지 저장/복원

Implementation (high-level)

Leveraging the interrupt stack convention

  • 인터럽트 루틴의 본체는 프로로그와 에필로그 사이에 위치하며, sp를 다른 메모리 영역으로 교체하면 다른 컨텍스트의 레지스터 집합을 복원하게 됨
    • 이는 곧 컨텍스트 스위치에 해당하며, 본 실험의 시분할 구현 핵심 아이디어
    • 타이머 인터럽트가 주기적으로 개입해 메인 흐름과 인터럽트 흐름을 교차 실행함

Kernel/user space separation

  • S-모드 커널 / U-모드 사용자의 경계를 유지하며, 인터럽트와 시스템 호출 처리는 S-모드 트랩 핸들러에서 수행
    • 부팅은 M-모드의 OpenSBIS-모드 커널 초기화U-모드 스레드 시작 순서로 진행
    • 주기적 타이머 인터럽트가 스케줄링·문맥 전환을 가능케 함

Implementation (code)

Assembly startup

  • startup.S에서 BSS 초기화와 초기 스택 포인터 설정Zig의 main으로 점프하는 최소 시퀀스 구성
    • 커널 진입점은 C ABI와의 연계를 위해 export 규약을 사용

Main kernel file and I/O drivers

  • kernel.zig의 main은 우선 OpenSBI 콘솔 기능을 점검하고, 실패 시 UART MMIO로 폴백함
    • sbi.debug_print는 ECALL 프로토콜에 맞춰 a0/a1/a6/a7 레지스터를 설정해 호출
    • 타이머 설정 후 S-모드 인터럽트 핸들러를 등록하고 틱을 활성화

S-mode handler and the context switch

  • 핸들러는 Zig의 naked 규약으로 작성해 CSR 보존을 포함한 완전한 프로로그/에필로그를 수동 구성
    • 본체에서 handle_kernel(sp)를 호출하고, 반환된 sp로 교체해 스위치 여부를 결정
    • scause로 U-모드 ECALL인지 타이머 인터럽트인지 판별해 분기 처리

The user space threads

  • 사용자 코드는 커널과 함께 단일 바이너리에 포함되며, 예시 스레드는 문자열 출력 → 지연 루프를 반복
    • syscall.debug_print는 a7에 64번 시스템 호출 번호, a0/a1에 버퍼/길이를 넣고 ECALL을 수행
    • 스레드 초기화 시 스택에 복귀 주소·초기 레지스터 값을 시딩해 첫 복귀 때 바로 인자 사용이 가능

Running the kernel

  • 빌드는 zig build, 실행은 QEMU에서 virt 머신 + nographic + OpenSBI fw_dynamic을 지정해 수행
    • 부팅 시 OpenSBI 배너 이후 스레드 ID별 주기적 출력이 교차 등장
    • -Ddebug-logs=true로 빌드하면 인터럽트 원천, 현재 스택, 큐잉·디큐잉 로그가 상세히 표시

Conclusion

  • 본 실험은 RISC-V + OpenSBI + Zig 조합으로 교육용 커널을 현대화해 재현성가독성을 높임
    • 최소한의 오류 처리와 과할당 스택 등 단순화가 있으나, 컨텍스트 스위치의 본질권한 분리 학습에 초점이 맞춰짐
    • 리얼 머신 이식성은 링커·드라이버 상수 조정과 SBI 가용성 확보를 전제로 가능

추가 메모: 가상화 스펙트럼 정리

  • Threads: 레지스터·스택 가상화 위주, 메모리 공유 가능성 높음
  • Process: 주소 공간 가상화로 메모리 격리, 내부에 다수 스레드 포함 가능성
  • Container: 파일시스템·네트워크 네임스페이스 등 환경 시야를 조합해 구성되는 격리 단위임
  • VM: 하드웨어 전반의 완전 가상화를 지향함

핵심 구현 포인트 요약

  • 인터럽트 스택 교체컨텍스트 스위치 실현
  • S-모드 트랩 핸들러에서 CSR 포함 전체 상태 보존/복원
  • SBI 우선, UART MMIO 폴백의 출력 경로 이중화
  • 정적 스레드·단일 코어·타임 슬라이스 중심의 단순 스케줄링
  • ECALL 기반 시스템 호출U/S 경계 명확화

Read Entire Article