March 7, 2012

透過 L4Ka 快速打造作業系統雛型

下標題時,其實頗為掙扎,開發作業系統如此重要之事,怎能講求速成呢?不過,考量到 目前無論是雲端或者移動裝置,都有比例可觀的技術是建構在已有成熟的作業系統之上, 探討實做一個作業系統的 Prototyping (雛型方法),何嘗不是引入創新的機會。Prototyping 是在 1980 年代初期興起的一種軟體發展模式,動機在於欲求在限定期限內,以最經濟而 快速的方法開發出系統的原型,以便即早澄清或驗證不明確的系統需求。本文嘗試在 [L4Ka] microkernel 的基礎上,建構一個適用於 IA32 架構的小型作業系統。

在稍早的演講議程 [L4 microkernel 的背景知識與最新的研究發展] 中,筆者提及 L4 家族中 L4Ka 專案企圖以 C++ 高階語言及物件導向描述方式,重作 Jochen Liedtke 博士提出的 L4 microkernel 原型,這部份的成果也就是 [L4Ka::Pistachio],目前一系列的專案原始碼均已在 [github] 上維護。L4Ka 不僅實做 L4 ABI,還提供了眾多核心開發的機制,如 in-kernel debugger,以及眾多的 user-level 執行時期支援,這使得我們很容易在上方作擴充。過去猶他大學曾有個 [OSKit] 專案,企圖以抽象化軟體元件的形式,提供作業系統所需的功能,達到 Prototyping 的目的,可惜自 2002 年後,OSKit 即不再維護,但 L4Ka 仍活躍地支持多樣的硬體架構,這是本文作選擇的考量。

以下將示範如何在 x86/PC 實做 "Hello World" 等級的作業系統,這與前文 [開機見 Hello World] 所不同之處是,我們不需要接觸到任何一行組合語言,而且也不需要充分理解 IA32 架構。筆者的環境是 Ubuntu Linux 12.04 x86 32-bit,相關的系統套件如下:
  • gcc 4.6.3
  • qemu-system-i386 1.0
首先,在 /tmp 建立我們的工作目錄,並取得 L4Ka 的原始程式碼:
cd /tmp
mkdir -p myos
cd myos
git clone git://github.com/l4ka/pistachio.git l4ka-pistachio
然後我們可在 l4ka-pistachio/user/apps/ 目錄下見到若干個 user-level 程式,而我 們的 "myos" 即將建立於此,所以先在檔案 l4ka-pistachio/user/apps/Makefile.in 追 加編譯項目,如下:
SUBDIRS=	bench grabmem l4test myos
因為 L4Ka 使用 Automake,所以也得一併修改檔案 l4ka-pistachio/user/configure.in
...
dnl Modified files.
AC_CONFIG_FILES([
config.mk
Makefile
...
apps/bench/pingpong/Makefile
apps/grabmem/Makefile
apps/l4test/Makefile
apps/myos/Makefile
...
隨後就可以開始打造這個 "Hello World" 等級的玩具作業系統,先參照 grabmem 的程式碼:
mkdir -p l4ka-pistachio/user/apps/myos
cd l4ka-pistachio/user/apps/myos
sed -e 's/grabmem/myos/g' ../grabmem/Makefile.in > Makefile.in
這個 Makefile.in 透漏一點線索:
PROGRAM=	myos
PROGRAM_DEPS=	$(top_builddir)/lib/l4/libl4.a \
		$(top_builddir)/lib/io/libio.a

SRCS=		crt0-$(ARCH).S myos.cc

LIBS+=		-ll4 -lio
LDFLAGS+=	-Ttext=$(ROOTTASK_LINKBASE)
我們需要針對平台的 crt0 (表示 C Run-Time,而 "0" 則是一開始之意),作為 C 語言 main() 進入點的必要準備工作,這裡我們直接從 grabmem 複製:
cp ../grabmem/crt0-ia32.S .
不過這份 x86 startup code 倒也不難懂,快速瀏覽一下:
	.text
	.global _start
	.global _stext
_stext:	
_start:
0:	leal	stack, %esp
	pushl	$___return_from_main
	jmp	main
 
___return_from_main:
	int $3
	jmp 1f
	.ascii "System stopped."
1:	jmp ___return_from_main
 
	.bss
 
	.align	16
	.space	4096 * 2
stack:
究竟我們這個 "MyOS" 與 L4Ka 的關聯何在?下方的圖例說明互動關係:

除了 L4Ka::Pistachio 外,我們可見到 Sigma0 RPC 通訊協定,這是 L4 microkernel 除了 FastIPC 外,另一個主要的設計,用以處理記憶體管理。舉例來說,倘若系統存在兩個不同 address space 的執行單元 (如 UNIX Process),而其中一者 (userA) 想要存取另一者 (userB) 的記體體,只要將 userA 設置為 userB 的 pager (記憶體管理單元) 並提供 userB 的 page fault handler 即可。其中,系統初始的 pager 就是 sigma0 (在某些 L4 的實做已移除),而 MyOS 在 L4 的術語叫做 root task / server,但若是 Linux 想達到相似功能,就得透過 shared memory,但便利性與效能均不若 L4 來得好。

我們不打算在第一個版本作太多動作,MyOS 的程式碼就只有以下這幾行:(檔案 /tmp/l4ka-pistachio/user/apps/myos/myos.cc )
#include <l4io.h>
#include <l4/sigma0.h>

static int test_kip() {
    printf("Testing L4 kernel interface page...\n");
    L4_KernelInterfacePage_t *kip =
        (L4_KernelInterfacePage_t *) L4_KernelInterface();
    printf("L4 version: %X.%X\n",
        kip->ApiVersion.x.version,
        kip->ApiVersion.x.subversion);
    return 0;
}

int main() {
    printf("MyOS is now launched!\n\n");
    test_kip();

    while (1)
        L4_Sleep(L4_TimePeriod(1000 * 1000));
    return 0;
}
開始自原始程式碼建構 L4Ka:(以 bash 為例)
cd /tmp/myos
rm -rf build
mkdir -p build/out build/user
export ROOT=`pwd`
cd l4ka-pistachio/kernel
make BUILDDIR=${ROOT}/build/kernel
cd ${ROOT}/build/kernel
make batchconfig
make
因為稍早我們修改過 Automake 的編譯項目,所以要重新生成:
$ cd ${ROOT}/l4ka-pistachio/user
$ autoheader
$ autoconf
最後,終於可以編譯 MyOS 程式碼:(user-level)
cd ${ROOT}/build/user
${ROOT}/l4ka-pistachio/user/configure \
    --with-comport=0 --with-comspeed=115200 \
    --prefix=${ROOT}/build/out \
    --with-kerneldir=${ROOT}/build/kernel \
    --host=x86
make install
cd ${ROOT}
我們可以注意到 build 目錄底下的兩個檔案:
  • build/kernel/x86-kernel : L4Ka microkernel 的 ELF image
  • build/user/apps/myos/myos : MyOS 的 ELF image (root task/server)
我們可以建立 floppy image,並將 L4 與 MyOS 置放其中。不過我們還需要一個符合 multiboot specification 的 boot loader,比方說 grub,本文採用 grub 0.97 版本 (注意:grub 0.97 與所謂 grub2 的 1.9x 版本,在系統中彼此是互斥的)。一旦自 grub 接到系統控制權後,L4Ka 的啟動程序 'kickstart' 會將 sigma0, sigma1, root server 都載入到記憶體中,並將這些的起始位址、結束位址,以及自 grub 啟動而得到的實體記 憶體位址等資訊,都寫入到 KernelInterfacePage (kip)。值得注意的是,依據 L4 規範, KernelInterfacePage 的定義中並未包含 sigma0, sigma1, root server 等資訊的定義 ,僅指定若干位定義的空間,而 L4Ka::Pistachio 的 IA32 版本就使用這些空間,作為 sigma0 等模組的儲存。詳細的記憶體配置資訊,可參考 [The boot process on IA-32]。

我們需要先編輯 /tmp/grub.conf 檔案,作為 grub 設定之用:
root (fd0)
default 0
timeout 3

serial --port=0x3f8 --speed=115200
terminal --timeout=0 serial

title MyOS
kernel /kickstart
module /x86-kernel
module /sigma0
module /myos                                                                                             

假設 grub 安裝於 /boot/grub 目錄,可以如下操作:
mkdir -p fdsource
mkdir -p fdsource/boot/grub
cp build/out/libexec/l4/{kickstart,sigma0,myos} fdsource/
cp build/kernel/x86-kernel fdsource/
cp grub.conf fdsource/boot/grub/menu.lst
cp /boot/grub/stage? fdsource/boot/grub
dd if=/dev/zero of=fdimage.img bs=512 count=2880
echo 'drive a: file="fdimage.img"' > mtoolsrc
MTOOLSRC=./mtoolsrc mformat -f 1440 a:
MTOOLSRC=./mtoolsrc mmd a:/boot
MTOOLSRC=./mtoolsrc mmd a:/boot/grub
MTOOLSRC=./mtoolsrc mcopy fdsource/boot/grub/stage1 a:/boot/grub
MTOOLSRC=./mtoolsrc mcopy fdsource/boot/grub/stage2 a:/boot/grub
MTOOLSRC=./mtoolsrc mcopy fdsource/boot/grub/menu.lst a:/boot/grub/
MTOOLSRC=./mtoolsrc mcopy fdsource/x86-kernel a:/
MTOOLSRC=./mtoolsrc mcopy fdsource/kickstart a:/
MTOOLSRC=./mtoolsrc mcopy fdsource/sigma0 a:/
MTOOLSRC=./mtoolsrc mcopy fdsource/myos a:/
接下來就是安裝 grub 到這個 floppy image:
echo "(fd0) fdimage.img" > bmap
echo -e "root (fd0)\nsetup (fd0)\nquit\n" | \
    /boot/grub/grub --batch --device-map=bmap
沒意外的話,檔案 fdimage.img 就是我們最終的 MyOS 開機軟碟檔案,用 QEMU 來驗證:
qemu-system-i386 -fda fdimage.img
稍微等待,會得到以下畫面:

上圖就是 grub 將控制權交給 KickStart 之後,陸續載入 L4Ka, sigma0, root server 等模組。一旦我們按下 Ctrl-3 組合鍵,將 QEMU 顯示切到 serial 輸出時,會得到以下 輸出:
Detected multiboot compliant loader
 kernel    (0x00819000-0x0085ae72)   => 0x0015b050
  (0x00819000-0x00829255) -> 0x00100000-0x00110255
  (0x0082a000-0x0083013e) -> 0x00112000-0x0011813e
  (0x00831000-0x0084f428) -> 0x00119000-0x00137428
  (0x00850000-0x00853ab8) -> 0x00158000-0x0015bab8
 sigma0    (0x0085b000-0x00875f15)   => 0x00020000
  (0x0085c000-0x00865524) -> 0x00020000-0x00029524
 roottask  (0x00876000-0x00881597)   => 0x0040009c
  (0x00877000-0x0087c044) -> 0x00400000-0x00405044
Launching kernel ...

L4Ka::Pistachio - built on Mar  7 2012 14:57:35
MyOS is now launched!

Testing L4 kernel interface page...
L4 version: 84.5
終於看到 MyOS 跑起來,雖然乍看只做到「睡美人」的作用,但背後反映了 L4 基礎的操作,而且我們也可在此基礎作 Prototyping。
由 jserv 發表於 March 7, 2012 12:33 AM
迴響
發表迴響









記住我的資訊?