January 11, 2007

透過 User-Mode-Linux 來學習核心設計 (1)

因為收到不少網友來信指教,小弟決定整理過去的心得與筆記,分享如何透過 [User-Mode Linux] (以下簡寫 UML,注意該術語與軟體工程的 Unified Modeling Language 無任何關係) 來學習核心設計、體驗修改並驗證的新途徑。

UML 顧名思義是將 Linux Kernel 移植到 user-space,如此一來,就可將這個修改的 "Kernel" 當作一般的 Linux process 來執行,這有什麼好處呢?簡單來說,至少有以下應用:
  • 對與硬體架構無關的一般性 Linux Kernel 程式作偵錯與快速測試
  • 檢驗 file system 的完整性與正確性,特別是 init script 相關的部份
  • 在單機建構虛擬網路環境,以多個網路單元進行模擬操作
  • 追蹤 Linux Kernel 大體流程,允許快速測試新的演算法或改進途徑
  • 完整的 Linux 教學環境
在 UML 官方網頁 [What are people using it for?] 與 [Case Studies] 有更詳細的描述。類似的技術有 Win32 平台的 [coLinux] (Cooperative Linux) 與 FreeBSD jail (指 sandbox 的行為) 等。如果不能從以上描述體會 UML 的重要性,不妨想想這個案例:做 Embedded Linux 最痛苦的不是在設計階段,而是測試與調整的步驟,以前我們很直覺會利用硬體環境直接測試,不是放到 Floppy,就是弄到 Flash 之類的實體環境,光是 download 就耗費相當多時間了,更別說發現 Bug,然後 rebuild 再重來,曠日費時。所以呢,比較妥當的方式是利用模擬的環境,像是 chroot 指令的使用,不過,那也要 target 環境跟 building 系統接近才行,那,不同的 kernel 組態怎麼辦呢?或是,很容易毀損實體 filesystem 的測試呢?透過 UML,我們可以建構出一個極佳的測試環境。

知名的 [LEAF] (Linux Embedded Appliance Firewall) 計劃的文件中,有份 [Developing and using LEAF in a virtual environment] 開發文獻很值得一看,不僅克服不必要的微調動作,還能兼具設計與測試雙軌運作的趨勢,透過建構 UML 作為基礎的開發環境,並讓 [LEAF] 能在其上開發。

[UserModeLinux for KNOPPIX] 很有創意也相當實用的途徑,透過 UML 來測試不同的 [Knoppix Linux LiveCD 配置組態,可以避免無謂的燒錄測試,而且可以透過 UML 搭配特定 debugger 與 benchmark profiling 來進行微調。

無獨有偶,[GPE] (GPE Palmtop Environment) 官方網站文件中也提及這類 cross-development 的方式,請見 [GPE: UML GTK 2 cross compiling environment],如此一來,ARM target 的 layout 就可以在設計的初期界定了,相當方便的途徑,當然,隨著 [qemu] 具備 ARM system emulation 後,使用 UML 的途徑就稍微彆扭些,但核心的想法是雷同的。

也因此,Linux Kernel 2.6 source tree 正式收錄 UML,我們也不再需要跟一堆 patch 搏鬥,不過 UML 經歷過許多重大修改,這使得不同時期的 HOW-TO 文件幾乎都有重大出入 (早於 2004 年的文件就不需要太拘泥細節了,畢竟設定方式更動過大),對一般使用者來說,徒增困擾而難以入門,這也是為何小弟試圖撰寫這系列筆記的原因,需要留意的是,因為 Linux Kernel 總是捨棄相容性包袱,所以難確保有一致的文件,也因此,本系列文件基本上以新版修正舊版缺失的方式撰寫,對系統造成的損失或不便處,請多見諒,但歡迎 [來信指教]。以下是本系列文件著墨之處 (不依據時間順序):
  • 親手打造 UML 開發環境
  • 建構 UML 所需之 root file system 並微調
  • 以 GDB 追蹤 UML
  • 修改 UML 並快速驗證,體驗 Kernel Hacking 的快感
  • 以 UML 建構虛擬網路環境,進而實驗小型 PC Cluster
UML 是種虛擬機器機制,可建立在既有 Linux 作業系統上多個 Linux 系統的「假象」,而虛擬機器所使用的 rootfs (root filesystem) 則建立於宿主系統上的單一檔案系統。一般而言,程序執行於 Linux 的架構如下圖:

而 UML 則是在宿主系統上另執行一核心,如下圖:

由此可見,UML 提供的虛擬機器,允許模擬出比實體裝置更豐富的配置方式,而且 UML 所使用的檔案系統對宿主 Linux 來說也不過只是單純的檔案,一切都好比置身於保護的 sandbox (原文的意思就是「貓沙盒」,給調皮的貓咪一個自得其樂卻不傷害家具的器具,引申為保護的自我封裝機制),經由適當配置,我們大可放心對虛擬機器作任何更動,而不必擔憂損害到真實的硬體與系統。

相當重要的觀念是:UML 本身就是全功能的 kernel,具備專屬的虛擬環境,對硬體的支援僅仰賴於宿主 Linux 系統,從 UML 內部的觀點來看,基本上支援基本的硬體裝置,如:
  • block device
      UML 的 block device 其實是透過檔案以映射到宿主環境中的檔案系統,仍秉持 UNIX 一貫的 "Everything is file" 的概念,UML 可如掛載實體硬碟裝置一般,將檔案系統掛入,當然也可建立 swap,更可如 Raw disk 一般被處理,透過 dd 一般的工具進行讀寫。UML 的 block device 可分層處理,並透過 CoW (Copy-on-Write) 的方式,作有效率且安全的操作,同時這允許我們執行多個 UML 實體,卻可避免頻繁的系統崩潰造成檔案系統的損毀。
  • console & serial
      幾乎沒改變,與 Linux kernel 共享絕大部份的程式碼,差異在於由 kernel 提供不同的介面。就 UML 目前的設計來說,提供 file descriptor、ptys、ttys、pts 和 xterm。預設的 console file descriptor 為 0 (stdin) 與 1 (stdout),其他則是 xterm 彩色終端機來啟動。
  • network device
      UML 內建類似 Hub 的軟體實做,以轉發封包,可從一個虛擬機器遞送到另一個,或設定為多點傳輸,UML 可在主機使用 ethertap 或 slip 介面。
  • SCSI
      UML 可模擬小型 SCSI 裝置。
  • USB / Sound / PCI / Wifi
      皆有實驗性的模擬途徑被提出,目前進度算是堪用,但並未完整整合到 UML 官方開發,需要再行確認。
Debian GNU/Linux 很早就收錄 UML 與其相關的工具,不過為了能深入體驗 UML,我們試著 build from scratch,這裡採用的 Linux Kernel 版本為 2.6.19.1 (Date: 2006-12-11),可在 [Kernel.org] 下載,建議用 "linux-2.6.19.1.tar.bz2" 來搜尋 mirror,畢竟現在的 kernel 已經增長到 41 Mb 的龐大空間。儘管 UML 已經整合到 2.6 source tree,但每個發佈版本總不免會有小疏失,所以請一併取得小弟的修正 [uml-2_6_19_1-compilation.patch],以及預先做好的組態設定值 [uml-dot-config] (使用 ext2 檔案系統)。大致的建構方式如下:
$ tar jxvf linux-2.6.19.1.tar.bz2
$ cd linux-2.6.19.1
$ patch -p1 < ../uml-2_6_19_1-compilation.patch
$ cp ../uml-dot-config .config
$ make menuconfig ARCH=um
$ make linux ARCH=um
注意到包含 "make" 的那兩行都有 "ARCH=um" 的 build variable,這是建構 UML 一定要加上的,無論是 kernel source 或 external kernel module,都需在 make 時加上,至於原因,看官花點時間看 Linux Kernel 的 Makefile 就可見其端倪。不過,就算不小心忘了加上,請立即執行以下操作:
$ make mrproper
經過冗長的編譯過程後,會發現有個新檔案被建立,即 "linux",但別急著執行,因為我們需要合用的 rootfs,原本這是苦差事,但 Debian/Ubuntu 有個強大的工具 [debootstrap] 可輕易生成具備 Debian/Ubuntu base 的 rootfs,以下是套件簡介:
    debootstrap is used to create a Debian base system from scratch, without requiring the availability of dpkg or apt. It does this by downloading .deb files from a mirror site, and carefully unpacking them into a directory which can eventually be chrooted into.
為了避免浪費時間在細節上,我準備了一個 script [create-uml-rootfs] 與部份設定檔範例 [etc_sample.tar.bz2],以下是 script 內容:
#!/bin/sh

BASE_DIR=`pwd`

# --- Modified as you need ---
TARGET_DIR=$BASE_DIR/ext2fs
ETC_SAMPLE=etc_sample.tar.bz2
ROOTFS_FILE=ubuntu-root

# Create rootfs (400Mb)
echo "Creating root file system..."
rm -f $ROOTFS_FILE
dd if=/dev/zero of=$ROOTFS_FILE bs=1024K count=400
if [ ! -f $ROOTFS_FILE ]; then
    echo "Error: creation of image file fails."
    exit
fi
yes y | mkfs.ext2 $ROOTFS_FILE
mkdir -p $TARGET_DIR
mount -o loop $ROOTFS_FILE $TARGET_DIR

# Make use of Debian's debootstrap tool to construct Ubuntu Dapper (6.10) base
echo "Invoking debootstrap..."
debootstrap --arch i386 dapper \
    $TARGET_DIR \
    http://archive.ubuntulinux.org/ubuntu

# Extract sample configure files
if [ -f $ETC_SAMPLE ]; then
    cd $TARGET_DIR
    tar jcvf $ETC_SAMPLE
    cd $BASE_DIR
fi

# mknod for ubd0 (specific to UML)
if [ -d $TARGET_DIR/dev ]; then
    cd $TARGET_DIR/dev
    mknod --mode=660 ubd0 b 98 0
    chown root:disk ubd0
    cd $BASE_DIR
else
    echo "Error: debootstrap fails."
    exit
fi

# Finish
sync
umount ext2fs
echo "Done"
echo "Please assign the rootfs: " $ROOTFS_FILE
沒有意外的話,以 root 權限執行以上 script 後,[debootstrap] 會幫我們建構具備 Ubuntu Dapper (6.10) base 的 rootfs,其容量為 400 Mb。注意 [debootstrap] 下載 Debian package 是透過 wget,如果在防火牆的環境中,請將相關設定準備好。輸出的 "ubuntu-root" 檔案即是 UML 所需的 rootfs,是的,就是一個檔案,損毀的話就再重跑以上流程,無論 UML 內部發生什麼事情,宿主 Linux 仍沒有大影響。

由於後續操作都會大量使用終端機,請確認安裝合用的終端機,如 [rxvt-unicode] 就是一個不錯的選擇。我們將剛剛產生的 "ubuntu-root" 檔案放在 linux-2.6.19.1 目錄下,於是可進行啟動程序:
./linux ubd0=`pwd`/ubuntu-root
ubd 也就是 UML Block Device 之意,對 UML 相當重要,將透過該 device 存取到 rootfs,我們應該會見到類似以下輸出:
Checking that ptrace can change system call numbers...OK
Checking syscall emulation patch for ptrace...OK
Checking advanced syscall emulation patch for ptrace...OK
Checking for tmpfs mount on /dev/shm...OK
Checking PROT_EXEC mmap in /dev/shm/...OK
Checking for the skas3 patch in the host:
  - /proc/mm...not found
  - PTRACE_FAULTINFO...not found
  - PTRACE_LDT...not found
UML running in SKAS0 mode
Checking that ptrace can change system call numbers...OK
Checking syscall emulation patch for ptrace...OK
Checking advanced syscall emulation patch for ptrace...OK
[42949372.960000] Linux version 2.6.19.1 (jserv@venux) (gcc version 4.1.2 20070106 (prerelease) (Ubuntu 4.1.1-21ubuntu7)) #4 Wed Jan 10 20:53:03 CST 2007
[42949372.960000] Built 1 zonelists.  Total pages: 8128
[42949372.960000] Kernel command line: ubd0=/opt/src/ubuntu-root root=98:0
[42949372.960000] PID hash table entries: 128 (order: 7, 512 bytes)
[42949372.960000] Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
[42949372.960000] Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
[42949372.960000] Memory: 30100k available
[42949373.230000] Mount-cache hash table entries: 512
... 省略 ...
預設 root 是不需要密碼,我們試著登入並結束系統:
Ubuntu 6.06 LTS uml tty0

uml login: root
[42949456.230000] line_ioctl: tty0: unknown ioctl: 0x5603
Last login: Wed Jan 10 16:47:59 2007 on tty0
Linux uml 2.6.19.1 #4 Wed Jan 10 20:53:03 CST 2007 i686 GNU/Linux
root@uml:~# halt

Broadcast message from root@uml (tty0) (Wed Jan 10 16:48:40 2007):

The system is going down for system halt NOW!
 * INIT: Switching to runlevel: 0
 * INIT: Sending processes the TERM signal
hwclock is unable to get I/O port access:  the iopl(3) call failed.
 * Stopping kernel log... [ ok ] 
 * Stopping system log... [ ok ] 
 * Terminating any remaining processes... [ ok ] 
 * Unmounting remote filesystems... [ ok ] 
 * Deconfiguring network interfaces... [ ok ] 
 * Unmounting local filesystems... [ ok ] 
 * Deactivating swap... [ ok ] 
 * Will now halt
[42949503.180000] System halted.
粗體字是我們鍵入的部份,當有 ioctl 抱怨錯誤訊息,請忽略,因為 UML 並未完全實做該功能,這不影響我們的進行。這短暫的過程我們見到 UML 虛擬機器的啟動與終結,佛語常說,一切萬物,自有緣起緣滅之時,且讓我們來探究 UML 的因果與深入設計,進而體驗 Linux Kernel 的美妙。
(待續)
由 jserv 發表於 January 11, 2007 12:59 AM
迴響

兩張圖重複了,第二張圖應該換成 uml_part_2.png

yaocl 發表於 January 11, 2007 09:28 AM

To yaocl,

Fixed now. Thanks!

jserv 發表於 January 11, 2007 10:37 AM

create-uml-rootfs 有一點小 typo。
*** create-uml-rootfs 2007-01-15 11:40:29.000000000 +0800
--- create-uml-rootfs.orig 2007-01-11 13:00:02.000000000 +0800
***************
*** 28,35 ****
# Extract sample configure files
if [ -f $ETC_SAMPLE ]; then
cd $TARGET_DIR
! tar jxvf $BASE_DIR/$ETC_SAMPLE
! cd $BASE_DIR
fi

# mknod for ubd0 (specific to UML)
--- 28,35 ----
# Extract sample configure files
if [ -f $ETC_SAMPLE ]; then
cd $TARGET_DIR
! tar jxvf $ETC_SAMPLE
! cd $BAE_DIR
fi

# mknod for ubd0 (specific to UML)

Solomon 發表於 January 15, 2007 12:23 PM

To Solomon,

Fixed now. Thanks!

jserv 發表於 January 15, 2007 01:55 PM

arch/i386/lib/semaphore.S:34: Error: unknown pseudo-op: `.cfi_startproc'

make kernel error . My linux is Redhat9. Why?

wenhsuan 發表於 January 26, 2007 03:40 PM

To wenhsuan,

Please do "make menuconfig ARCH=um", and disable the option "Compile the kernel with frame unwind information" in "Kernel hacking" section.

Thanks for your feedback.

jserv 發表於 January 27, 2007 05:33 PM

thanks, I do make successful as you said.

wenhsuan 發表於 January 30, 2007 12:21 PM

Your introduction of UML linux is very good. I will give a try.
By the way,
many IC EDA tools only can run in RedHat 8.

can I use UML method to treat REDHAT 8 in CentOS4.4?

Hugo 發表於 February 1, 2007 02:46 PM

請問一下 Jserv, 利用UML可否存取到實體硬體?
例如, Host Linux 沒有掛上USB Stack Driver
而在UML掛上USB Stack Driver, 則是否在UML上可以存取USB Device.
謝謝

H. Johnny 發表於 April 28, 2009 03:22 PM

etc_sample.tar.bz2中似乎有個小typo:

etc/fstab:

/dev/udb/0 是否應該修改為 /dev/ubd/0

:)

koukai 發表於 December 10, 2009 12:10 AM
發表迴響









記住我的資訊?