Rust와 WebAssembly 샌드박스를 활용한 Munal-OS 유니커널 애플리케이션 개발

Munal-OS 아키텍처와 WebAssembly의 역할

Munal-OS는 Rust로 구현된 실험적인 유니커널(Unikernel) 운영체제입니다. 이 시스템의 핵심은 협력적 스케줄링과 WebAssembly(WASM) 샌드박스를 기반으로 한 보안 모델에 있습니다. 기존 OS가 복잡한 프로세스 격리와 메모리 보호 메커니즘에 의존하는 반면, Munal-OS는 WASM의 메모리 격리 특성을 활용해 애플리케이션 수준의 보장을 제공합니다.

WASM을 채택함으로써 얻는 기술적 이점은 다음과 같습니다:

  • 메모리 안전성: 샌드박스 환경에서의 포인터 격리로 인한 불법 메모리 접근 차단
  • 다중 언어 지원: WASM 타겟으로 컴파일 가능한 모든 언어(Rust, C, C++, AssemblyScript 등) 활용
  • 실행 성능: 네이티브에 근접한 JIT/AOT 컴파일 실행 속도
  • 이식성: 아키텍처에 독립적인 바이트코드 실행

커널 내부의 wasmi 인터프리터 또는 JIT 엔진이 WASM 모듈을 로드하며, 애플리케이션은 호스트 함수(Host Functions)를 통해서만 시스템 리소스에 접근할 수 있습니다.

개발 환경 구성

Munal-OS 타겟으로 빌드하기 위해 Rust 툴체인을 설정합니다.

# 저장소 복제
git clone https://github.com/munal-os/munal-os.git
cd munal-os

# WASM 타겟 및 소스 컴포넌트 설치 (Nightly 툴체인 사용)
rustup target add wasm32-wasip1 --toolchain nightly
rustup component add rust-src --toolchain nightly

프로젝트 디렉토리 구조

소스 코드는 다음과 같은 모듈로 분리되어 있습니다:

munal-os/
├── apps/               # 사용자 공간 WASM 애플리케이션
│   ├── timer_app/      # 타이머 유틸리티
│   ├── renderer_3d/    # 3D 그래픽스 데모
│   └── shell/          # 커맨드 라인 인터페이스
├── syslib/             # 게스트 OS용 시스템 콜 래퍼
│   └── src/api.rs      # 호스트 함수 바인딩
└── core/               # 커널 및 WASM 런타임
    └── src/executor/   # 스케줄러 및 WASM 실행기

WASM 모듈 작성 및 라이프사이클

새로운 애플리케이션을 위해 apps/my_service 디렉토리를 생성하고 Cargo.toml을 구성합니다.

[package]
name = "my_service"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
syslib = { path = "../../syslib" }
ui_toolkit = { path = "../../ui_toolkit" }
tracing = "0.1"

Munal-OS의 실행 모델은 초기화 및 프레임 업데이트 루프로 구성됩니다. 외부로 노출해야 하는 ABI 함수는 다음과 같습니다.

// src/lib.rs
use syslib::logger;

#[no_mangle]
pub extern "C" fn bootstrap() {
    logger::init();
    tracing::info!("Service initialized successfully.");
}

#[no_mangle]
pub extern "C" fn tick(delta_time: f32) {
    // 매 프레임마다 호출되는 로직
    // 입력 처리, 상태 갱신, 버퍼 렌더링 수행
    process_events(delta_time);
}

fn process_events(dt: f32) {
    // 내부 상태 업데이트 로직
}

bootstrap은 모듈 로드 시 단 한 번 실행되며, tick은 커널의 스케줄러에 의해 지속적으로 호출됩니다.

시스템 API 연동

입력 장치 상태 읽기

syslib을 통해 하드웨어 추상화 계층(HAL)의 입력 데이터를 조회합니다.

let hid_state = syslib::input::poll_devices();
let cursor_coords = hid_state.pointer.position;
let is_primary_clicked = hid_state.pointer.buttons.primary;

UI 컴포넌트 렌더링

즉시 모드(Immediate Mode) UI 패턴을 활용한 위젯 배치:

use ui_toolkit::widgets::{ButtonProps, Rect};

let bounds = Rect::new(10.0, 20.0, 100.0, 40.0);
let is_triggered = ui_context.render_button(&ButtonProps {
    area: bounds,
    text: "Execute".into(),
    style: Default::default(),
});

if is_triggered {
    tracing::debug!("Action triggered by user interaction.");
}

네트워크 소켓 통신

TCP 스트림을 통한 외부 호스트와의 데이터 교환:

// 소켓 연결 수립
let socket_fd = syslib::net::open_tcp_stream([10, 0, 0, 5], 9000)?;

// 페이로드 전송
let payload = b"SYN_REQUEST";
let bytes_sent = syslib::net::send_buffer(socket_fd, payload)?;

// 응답 수신
let mut recv_buf = [0u8; 512];
let bytes_read = syslib::net::recv_buffer(socket_fd, &mut recv_buf)?;

프로파일링 및 리소스 관리

실행 시간 측정

WASM 명령어 실행 수(Fuel)를 측정하여 성능 병목 현상을 분석합니다.

use syslib::profiler::track_fuel;

let consumption = track_fuel!("matrix_multiplication", {
    compute_heavy_math()
});
tracing::warn!("Fuel consumed: {}", consumption);

메모리 및 프레임 최적화

  • 가비지 컬렉션: WASM의 선형 메모리는 자동으로 관리되지만, 호스트로 전달되는 버퍼의 수명을 명시적으로 제어해야 합니다.
  • 프레임 드랍 방지: tick 함수 내부의 동기적 블로킹을 피하고, 무거운 연산은 청크 단위로 분할합니다.

빌드 및 커널 통합

WASM 바이트코드를 생성하고 커널의 루트 파일시스템에 마운트합니다.

# 릴리즈 모드로 WASM 컴파일
cd apps/my_service
cargo +nightly build --target wasm32-wasip1 --release

# 커널 에셋 디렉토리로 복사
cp target/wasm32-wasip1/release/my_service.wasm ../../core/assets/modules/

이후 cargo run 또는 QEMU 실행 스크립트를 통해 Munal-OS 부팅을 진행하면 모듈이 자동으로 로드됩니다.

확장 아키텍처 설계

플러그인 인터페이스 정의

트레이트를 활용하여 동적으로 로드 가능한 모듈 시스템을 구축할 수 있습니다.

pub trait Extension {
    fn on_load(&mut self, ctx: &mut RuntimeContext);
    fn on_update(&mut self, dt: f32);
    fn on_unload(&mut self);
}

비동기 태스크 시뮬레이션

협력적 스케줄링 환경에서 Future 기반 비동기 로직을 구현하기 위한 상태 머신:

enum FetchState {
    Idle,
    WaitingForResponse(u32),
    ProcessingData(Vec<u8>),
    Finished,
}

struct AsyncFetcher {
    state: FetchState,
}

impl AsyncFetcher {
    fn advance(&mut self) {
        match self.state {
            FetchState::Idle => { /* 요청 시작 */ }
            FetchState::WaitingForResponse(fd) => { /* 논블로킹 확인 */ }
            _ => {}
        }
    }
}

태그: Rust WebAssembly Unikernel WASM OperatingSystem

8월 13일 15:51에 게시됨