目录

使用

VirtualPC & Hyper-V

VirtualBox

$ svn co http://www.virtualbox.org/svn/vbox/trunk vbox
$ cd vbox
$ ./configure --disable-hardening

LXC

$ sudo apt-get install vnc4server

QEMU

建置 QEMU

QEMU 1.0 預設會編譯成 PIE,這對舊版的 GDB 會有影響。強制開啟 IO thread。使用 Clang 編譯 QEMU 會出現以下訊息1)2)3),現在已能用 Clang 編譯。

In file included from /z/tmp/chenwj/qemu-1.0/user-exec.c:21:
/z/tmp/chenwj/qemu-1.0/dyngen-exec.h:64:20: error: global register variables are not supported
register CPUState *env asm(AREG0);
                   ^
1 warning and 1 error generated.

TCI 沒有用到 global register variable。

$ ../qemu-1.0/configure --target-list=i386-bsd-user --enable-tcg-interpreter --cc=clang \
  --extra-cflags="-v" --disable-smartcard-nss

User Mode

目前 QEMU 將 glib2.0 列為必要,The GTK+ Project4)

  1. 如果系統沒有安裝 glib,則自行安裝。
    $ apt-get install zlib1g-dev pkg-config libglib2.0-dev
    $ wget http://ftp.gnome.org/pub/gnome/sources/glib/2.28/glib-2.28.0.tar.gz; tar xvf glib-2.28.0.tar.gz
    $ mkdir build; cd build
    $ ../glib-2.28.0/configure --prefix=$INSTALL
    $ make install
  2. 下載 QEMU。
    # http://wiki.qemu.org/download/qemu-0.15.1.tar.gz
    $ git clone git://git.qemu.org/qemu.git
  3. 修改 configure。
    # glib support probe
    #if $pkg_config --modversion gthread-2.0 > /dev/null 2>&1 ; then
        # 用 pkg-config 查詢
        glib_cflags="-pthread -I$INSTALL/include/glib-2.0/ -I$INSTALL/lib/glib-2.0/include/"
        glib_libs="-L$INSTALL/lib -pthread -lgthread-2.0 -lrt -lglib-2.0"
        LIBS="$glib_libs $LIBS"
        libs_qga="$glib_libs $libs_qga"
    #else
    #    echo "glib-2.0 required to compile QEMU"
    #    exit 1
    #fi
  4. 編譯並安裝 QEMU。
    # config.log 可以用來檢查設定 QEMU 時哪裡出錯
    $ mkdir build; cd build
    $ ../qemu/configure --prefix=$INSTALL --target-list=i386-linux-user --enable-debug
    $ make; make install
  5. 如果 glib 是自行安裝。
    $ export LD_LIBRARY_PATH=$INSTALL/lib/ 
    # -m32 會編譯 32-bit 執行檔
    $ gcc -m32 hello.c -o hello
    $ qemu-i386 hello

SPARC

關於 SPARC V8 (32-bit)、V8PLUS (64-bit with 32-bit ABI) 和 V9 (64-bit) 架構的差異請見 ABI Compliance and Global Registers Usage in SPARC V8 and V9 Architecture。QEMU 對 v9 支援最完整 5)。64-bit with 32-bit ABI 和在 x86_64 上運行 x86 binary 不一樣。64-bit with 32-bit ABI 代表程式可以存取 64-bit 硬體資源,但其內存空間仍只侷限在 32-bit; x86 binary 運行在 x86_64 上不能存取 64-bit 硬體資源 6)

$ uname -a
Linux sparc 2.6.37-rc5-git #1 SMP Tue Dec 21 17:03:53 CST 2010 sparc64 sun4v UltraSparc T2 (Niagara2) GNU/Linux
$ file /bin/ls
/bin/ls: ELF 32-bit MSB executable, SPARC32PLUS, V8+ Required, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped
# 雖然機器是 64-bit,但是裝的是 32-bit OS
# --sparc_cpu=v9 改用 sparc64-linux-gcc 
$ configure --prefix=$INSTALL --target-list=i386-linux-user --sparc_cpu=v8plus

PowerPC

因為 PS3 記憶體不足,可能導致 GCC 編譯 QEMU 失敗 7)8)。改以 debug 模式編譯。

  1. 註解 assert。
    --- qemu-0.13.0/tcg/tcg.c.orig  2010-10-16 04:56:09.000000000 +0800
    +++ qemu-0.13.0/tcg/tcg.c       2011-02-24 09:46:28.796899001 +0800
    @@ -1031,7 +1031,7 @@
             def = &tcg_op_defs[op];
     #if defined(CONFIG_DEBUG_TCG)
             /* Duplicate entry in op definitions? */
    -        assert(!def->used);
    +        //assert(!def->used);
             def->used = 1;
     #endif
             nb_args = def->nb_iargs + def->nb_oargs;
    
  2. 以 debug 模式編譯,或是參考 http://cruxppc.org/viewvc/opt/branches/2.7/qemu/Pkgfile?revision=2267&view=markup9)
    $ configure --prefix=$INSTALL --target-list=i386-linux-user --enable-debug
    $ make install
  3. 拷貝 x86 函式庫供 powerpc 上的 qemu-i386 使用。函式庫必須放在以 lib 為名的目錄底下。
    $ mkdir $HOME/tools/lib
    $ cp /lib32/ld-linux.so.2 $HOME/tools/lib
    $ cp /lib32/libc.so.6 $HOME/tools/lib
    $ export LD_LIBRARY_PATH=$HOME/tools/lib
    $ qemu-i386 -L $HOME/tools hello

ARM

  1. 安裝交叉工具鏈。使用預編譯好的工具鏈。可以到 Sourcery G++ Lite Edition 下載。或者使用 crosstool-NG 建立工具鏈。
    1. 下載並安裝 crosstool-ng。
      # 預設安裝在 $HOME/x-tools 下。
      # 注意運行 QEMU 平台的內核,這裡要選較其為舊的內核建立工具鏈。
      # 請記得將 crosstool.config 更名為 .config。
      $ wget http://ymorin.is-a-geek.org/download/crosstool-ng/crosstool-ng-1.9.2.tar.bz2; tar xvf crosstool-ng-1.9.2.tar.bz2
      $ cd crosstool-ng-1.9.2;
      $ ./configure --prefix=$INSTALL
      $ make install 
    2. 建置交叉工具鏈。
      $ mkdir toolchain-build
      $ cp $INSTALL/lib/ct-ng-1.9.2/samples/arm-unknown-linux-gnueabi/* .
      $ mv crosstool.config .config
      $ ct-ng menuconfig
      $ ct-ng build
  2. 安裝運行時期函式庫。如果要安裝預編譯好的運行時期函式庫。可以到 linux-arm.org 下載。
    # mount cramfs 要有 root 權限。可以改採下面的作法,使用 fakeroot-ng。
    $ wget http://www.linux-arm.org/git?p=ael.git;a=blob_plain;f=filesystem/bin/alip-ael-armv6-full-reduced.cramfs;hb=2010q4
    $ fakeroot-ng
    # /sbin/cramfsck filesystem_bin_alip-ael-armv6-full-reduced.cramfs -x runtime/
    # cp runtime/lib/* /path/to/runtime/lib
    # exit
    $

    也可以使用 crosstool-NG,在建立工具鏈時同時也會得到運行時期函式庫。預設安裝在 $HOME/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sys-root/lib。PowerPC64 需要為 sys-root/lib64 和 sys-root/usr/lib64 建立軟鏈結,請 chmod u+w sys-root 和 chmod u+w sys-root/usr。

  3. 執行 QEMU。假設 runtime 裝在 /path/to/runtime/lib。
    $ qemu-arm -L /path/to/runtime hello
    $ qemu-ppc64 $HOME/x-tools/powerpc64-unknown-linux-gnu/powerpc64-unknown-linux-gnu/sys-root hello

System Mode

  1. 編譯 QEMU。
    $ wget http://download.savannah.gnu.org/releases/qemu/qemu-0.14.1.tar.gz; tar xvf qemu-0.14.1.tar.gz
    $ mkdir build; cd build
    # i386-softmmu 執行檔名稱為 qemu。將在 QEMU 1.0 修正。
    $ ../qemu-0.14.1  --prefix=$INSTALL --target-list=i386-softmmu,arm-softmmu
    $ make && make install
  2. 編譯內核映像。
    • Lab 2 編譯 kernel
      # 取得 arch/i386/boot/bzImage 和 vmlinux 兩個文件
      # kernel hacking 開啟 debug
      $ wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.20.tar.gz; tar xvf linux-2.6.20.tar.gz
      # http://blog.csdn.net/livingpark/article/details/3732679
      # 在 scripts/mod/sumversion.c 加上 #include <limits.h>
      $ cd linux-2.6.20
      # make mrproper 清理原始檔和設定
      $ make mrproper; make ARCH=i386 menuconfig; make ARCH=i386
  3. 準備 root file system。如果沒有 root 權限的話,可以安裝 fakeroot 或是 fakeroot-ng,或是用 buildroot
    • Create root filesystem step by step
      $ wget http://busybox.net/downloads/busybox-1.18.5.tar.bz2; tar xvf busybox-1.18.5.tar.bz2
      $ cd busybox-1.18.5
      # Enable Busybox setting/Build Options/Build busybox as static binary
      $ make menuconfig
      $ make; make install
      $ cd ..; fakeroot-ng;
      # mkdir root; cd root
      # mkdir dev bin
      # mknod dev/console c 5 1
      # mknod dev/tty2 c 4 2
      # mknod dev/tty3 c 4 3
      # mknod dev/tty4 c 4 4
      # cp ../busybox-1.18.5/busybox  bin
      # ln -s bin/busybox init
      # cd bin
      # ln -s busybox sh
      # cd ..
      # find . |  cpio -o -H newc | gzip > ../initramfs

      或是直接使用 QEMU 官網上的測試檔。

      $ wget http://wiki.qemu.org/download/arm-test-0.2.tar.gz; tar xvf arm-test-0.2.tar.gz
      $ cd arm-test
      # zImage.integrator 是內核文件。arm_root.img 是根文件系统。
      # gzip -dc arm_root.img | cpio -idv
      $ qemu-system-arm -kernel zImage.integrator -initrd arm_root.img -nographic -append "console=ttyAMA0"
      $ wget http://wiki.qemu.org/download/minix204.tar.bz2; tar xvf minix204.tar.bz2
      $ cd minix204
      $ qemu -m 8M -fda vfloppya.img -hda minix204.img -boot c -vnc 0.0.0.0:1
      $ wget http://wiki.qemu.org/download/linux-0.2.img.bz2
      $ bunzip2 linux-0.2.img.bz2
      # 使用 vncviwer 連至 server:1 即可得到輸出。
      # 使用非常類似的 2.6.20 config。
      # http://bellard.org/jslinux/linuxstart-20110820.tar.gz
      #qemu -S -kernel bzImage -hda linux-0.2.img -append "root=/dev/hda" -vnc 0.0.0.0:1
      $ qemu linux-0.2.img -vnc 0.0.0.0:1

退出 QEMU 操作为:Ctrl+Alt ,然后按下 X 键。注意,它不会提醒你是否要退出,而是直接退出,所以操作时要小心。

QEMU 的 LInux 映像檔裡包含 NBench,分數越高越好。要切換到 nbench 的目錄底下執行 nbench 這隻測試程式,否則會噴出以下訊息:

CPU:NNET--error in opening file!

製作 QEMU 官網上的硬盤映像。

  1. Create the RAM disk whose size is 64M.
    $ dd if=/dev/zero of=disk.img bs=4096 count=16384
  2. Partition the disk.
    $ /sbin/fdisk -C 16065 -H 255 -S 63 disk.img
  3. Format the filesystem.
     
  4. Copy file system.
     
  5. Build the bootloader by grub.
     
  6. Boot system with your new disk.
     

shutdown 和 reboot 不會導致重開機,exit 登出 shell 會導致重開機。linux-0.11 登出後不會重開機10)

# -no-shutdown -no-reboot: 登出後,出現 "machine restart" 之後停住。
(qemu) system_powerdown 無反應
(qemu) system_reset QEMU 停止運作
# -no-reboot: 登出後,離開 QEMU。(exit instead of rebooting)
(qemu) system_powerdown 無反應
(qemu) system_reset 關閉 QEMU
# -no-shutdown: 登出後,重啟系統。(stop before shutdown)
(qemu) system_powerdown 無反應
(qemu) system_reset 重啟系統
# 不加 -no-shutdown -no-reboot
(qemu) system_powerdown 無反應
(qemu) system_reset 重啟系統

安裝系統

$ wget http://releases.ubuntu.com/12.04.1/ubuntu-12.04.1-server-amd64.iso
$ qemu-img create -f qcow2 ubuntu_x86_64.qcow2 3G
$ qemu-system-x86_64 -m 1024M -boot d -cdrom ubuntu-12.04.1-server-amd64.iso \
  -hda ubuntu_x86_64.qcow2 -vnc 0.0.0.0:1
$ qemu-system-x86_64 -m 1024M -hda ubuntu_x86_64.qcow2 -vnc 0.0.0.0:1
# 使用預安裝的映像檔。
$ wget http://people.debian.org/~aurel32/qemu/armel/vmlinuz-2.6.32-5-versatile
$ wget http://people.debian.org/~aurel32/qemu/armel/initrd.img-2.6.32-5-versatile
$ wget http://people.debian.org/~aurel32/qemu/armel/debian_squeeze_armel_desktop.qcow2
$ qemu-system-arm -M versatilepb -kernel vmlinuz-2.6.32-5-versatile \
  -initrd initrd.img-2.6.32-5-versatile \
  -hda debian_squeeze_armel_desktop.qcow2 -append "root=/dev/sda1" -vnc 0.0.0.0:1
 
# 請到 http://ftp.de.debian.org/debian/dists/ 找類似 Debian6.0.4/main/installer-armel/current/images/versatile/netboot
# 的路徑,下載 initrd.gz。
$ qemu-img create -f qcow2 hda.img 3G
$ qemu-system-arm -M versatilepb \
  -kernel vmlinuz-2.6.32-5-versatile -initrd initrd.gz \
  -hda hda.img -append "root=/dev/ram" -vnc 0.0.0.0:1
# 安裝完後,關閉 QEMU。並改用底下指令開機。
$ qemu-system-arm -M versatilepb \
  -initrd initrd.img-2.6.32-5-versatile \
  -hda hda.img -append "root=/dev/sda1" -vnc 0.0.0.0:1
# 或是改從 console 輸出。
$ qemu-system-arm -M versatilepb \
  -initrd initrd.img-2.6.32-5-versatile \
  -hda hda.img -append "root=/dev/sda1 console=ttyAMA0" -nographic
# 下載 linux kernel tarball,解開後在 arch/arm/configs 找 config。
$ wget 'http://linux-arm.org/git?p=ael.git;a=blob_plain;f=kernel/config/config-ael-2011.06.00-realview-v7-smp-thumb;hb=2011.06'

Buildroot

# http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2
$ wget http://buildroot.uclibc.org/downloads/buildroot-2011.11.tar.gz; tar vxf buildroot-2011.11.tar.gz
$ cd buildroot-2011.11
# for kernel,請見 arch/i386/defconfig 或 arch/arm/configs。make help 可以看到更多資訊。
# 先產生 qemu 用的 default config。
$ make qemu_x86_defconfig
# 設定 linux kernel version。最後生成的 rfs 格式,initramfs。
# 注意編譯 kernel 時所用的 config 從哪裡來。
#
# - gzip, tar
# 
# - rpm
#
# - dropbear: openssh 會一直要求改密碼。
# 
$ make menuconfig
$ make
# 使用 QEMU 內部的 DHCP 以便存取網路。
$ vi output/target/etc/network/interfaces
auto eth0
iface eth0 inet dhcp
$ cd output/images/
# 建議先轉成 qcow2 以便動態增長硬盤大小。rootfs.qcow2 只能增長成原本硬盤映像的大小。
# 加大或減小硬盤大小之後或之前,必須重新在 VM 裡重切分割區。
$ qemu-img convert -O qcow2 rootfs.ext2 rootfs.qcow2
$ qemu-img resize rootfs.qcow2 +3G
$ qemu-img info rootfs.qcow2
image: rootfs.qcow2
file format: qcow2
virtual size: 3.2G (3399145472 bytes)
disk size: 159M
cluster_size: 65536
$ qemu-system-i386 -kernel bzImage -initrd rootfs.cpio -vnc 0.0.0.0:1
$ qemu-system-i386 -kernel bzImage -hda rootfs.ext2 -append "root=/dev/sda console=ttyAMA0" -nographic \
  -redir tcp:2222::22 \
  -redir tcp:3333::3333
# 登入後,修改密碼。
$ passwd new_passwd
# 修改密碼若失敗,請修改 /etc/sshd_config,在其中將 UsePrivilegeSeparation 開啟:
# UsePrivilegeSeparation yes 
$ ssh -p 2222 root@localhost
# 欲增加 3G 硬盤空間。
$ qemu-img create -f raw additional.raw 3G
# 先將已存在的硬盤映像轉成 raw 格式。
$ qemu-img convert -f qcow2 example.img -O raw example.raw
$ cat additional.raw >> example.raw
$ -serial mon:telnet::4444,server,nowait
# VM 裡要開啟 telnet server,telnetd。才可以在 host 下 telnet 連進 VM。 
$ telnet 127.0.0.1 4444

Monitor

# 有時候會遇到 vnc 無法開啟的情況,使用 screendump 輸出螢幕內容。
# 再用 http://www.online-convert.com 轉成 jpeg 觀看。
(qemu) screendump screenshot.ppm
(qemu) info jit
Translation buffer state:
gen code size       3785504/4089856
TB count            11244/32768
TB avg target size  14 max=595 bytes
TB avg host size    336 bytes (expansion ratio: 23.4)
cross page TB count 25 (0%) // guest TB 對映的 guest binary 跨 guest page 的個數和比例。
direct jump count   7569 (67%) (2 jumps=5173 46%)

Statistics:
TB flush count      0
TB invalidate count 2737
TLB flush count     13869
# 印出客戶機虛擬位址處的內容。
(qemu) x /2 0x08d31008
08d31008: 0x64636261 0x00006665
# 印出客戶機物理位址處的內容。
(qemu) xp /2 0x01603008
0000000001603008: 0x64636261 0x00006665
# 顯示內存、MMIO、IO 地址分佈。
# 以縮排顯示階層關係。
(qemu) info mtree
I/O
0000000000000000-000000000000ffff (prio 0): io
  0000000000000020-0000000000000021 (prio 0): pic

使用 Ctrl+Alt+2 组合键切换到 QEMU 终端 (QEMU Monitor),然后输入 gdbserver ,启动 gdbserver 服务。 这时启动另外一个终端窗口,输入 gdb vmlinux 命令进行调试:

(gdb) target remote localhost:1234               /*使用 gdbserver 进行调试命令*/

或是將 QEMU 終端導至標準輸出。

$ qemu -hda linux-0.2.img -vnc 0.0.0.0:1 -monitor stdio

Snapshot

  1. 先確定裝置格式支援 snapshot。raw 不支援 snapshot。Monitor
    (qemu) info block
    ide0-hd0: removable=0 io-status=ok file=linux-0.2.img ro=0 drv=raw encrypted=0
    ide1-cd0: removable=1 locked=0 tray-open=0 io-status=ok [not inserted]
    floppy0: removable=1 locked=0 tray-open=0 [not inserted]
    sd0: removable=1 locked=0 tray-open=0 [not inserted]
    (qemu) savevm
    Device 'ide0-hd0' is writable but does not support snapshots.
  2. 用 qemu-img 轉換硬盤映像成 qcow2 格式。
    $ qemu-img convert -O qcow2 linux-0.2.img linux-0.2.qcow2 
  3. 在 monitor 在下 savevm 把 RAM、device state 和 disk 存到當前使用的硬盤映像13)
    (qemu) savevm
    (qemu) loadvm
    (qemu) info snapshots

Network

QEMU 可以透過虛擬網卡連至 vlan,vlan 之間可以透過 slirp,socket,tap 或是 vde 連接。在 Documentation/Networking 提到的 virtual network device 即為虛擬網卡,network backend 負責將虛擬網卡送出的資料放到真實網路上,可以是 slirp,socket,tap 或是 vde。QEMU Networking 裡面的 usermode network stack 即為 slirp,slirp 為預設網路後端,見下圖。 QEMU 內部會啟動一個 DHCP 伺服器,宿主作業系統不可見。

  1. 存取宿主機的資料。
    # 在 host 執行底下命令。
    $ cd path/to/shared/files && python -m SimpleHTTPServer
    # 在 guest 執行底下命令。
    $ wget http://10.0.2.2:8000/README
  2. 存取客戶機的資料。
    # 將 host TCP 連線 127.0.0.1:8000 導到 guest 內部 DHCP 預設 IP 埠 8000。
    $ qemu-system-x86_64 -m 1024M -hda ubuntu_x86_64.qcow2 \
      -net user,hostfwd=tcp:127.0.0.1:8000-:8000 \
      -vnc 0.0.0.0:1
    $ wget localhost:8000/README
$ qemu-system-i386 -kernel bzImage-x86 -hda disk-x86.raw -append "root=/dev/sda" \
  -net nic -net tap,ifname=tap0,script=no -vnc :3 -monitor stdio

Tracing

請見 docs/tracing.txt。

$ wget http://download.savannah.gnu.org/releases/qemu/qemu-0.14.0.tar.gz
$ tar xvf qemu-0.14.0.tar.gz
$ vim qemu-0.14.0/trace-events
 
# qemu-malloc.c
qemu_malloc(size_t size, void *ptr) "size %zu ptr %p"
qemu_realloc(void *ptr, size_t size, void *newptr) "ptr %p size %zu newptr %p"
qemu_free(void *ptr) "ptr %p"
 
$ make install build; cd build;
$ ../qemu-0.14.0/configure --prefix=/path/to/install --target-list=i386-linux-user --enable-trace-backend=stderr
$ ./path/to/install/qemu-i386 hello

Cross Compile

  1. 需要 zlib 的 ARM binary。
    $ wget http://zlib.net/zlib-1.2.5.tar.gz; tar xvf zlib-1.2.5.tar.gz
    # export PATH=/PATH/TO/CROSSTOOL:$PATH
    $ CC=arm-none-linux-gnueabi-gcc ./configure \
      --prefix=$INSTALL
  2. 執行底下腳本。
    #!/bin/bash
     
    SOURCE_DIR=../user-mode
    ZLIB_PREBUILT=$HOME/x-tools
    TARGET_LIST=arm-linux-user
    CROSS_PREFIX=arm-none-linux-gnueabi-
    CPU=armv7l
    CFLAGS=-I$ZLIB_PREBUILT/include
    LDFLAGS=-L$ZLIB_PREBUILT/lib
    PREFIX=`pwd`
    OPT_FLAGS="--disable-sdl"
    DEBUG=--disable-strip
    STATIC=--static
     
    $SOURCE_DIR/configure --target-list=$TARGET_LIST \
      --cpu=$CPU \
      --extra-ldflags=$LDFLAGS  \
      --extra-cflags=$CFLAGS \
      --cross-prefix=$CROSS_PREFIX \
      --prefix=$PREFIX

Debug & Testing

$ git clone git://git.qemu.org/qemu-test.git
$ cd qemu-test
$ git submodule update --init
$ make

舊版 GDB 無法處理 PIE。

$ ../qemu-1.0/configure --prefix=$INSTALL --target-list=i386-softmmu --enable-debug --disable-pie
(gdb) handle SIG38 noprint pass
# 定位感興趣的 guest pc 以便查找其在 code cache 中的位址。
(gdb) b gen_intermediate_code_internal if tb->pc == 0x801a3bc
# 此時已生成 host binary,查找 qemu.log。
(gdb) b tb_find_fast

QEMU 內部會註冊 SIGNAL 供自己使用,例如 IO thread 執行完畢之後會發送 SIGNAL 給 vcpu thread。

  1. 在 qemu_tcg_init_cpu_signals (cpus.c) 註冊 QEMU 內部使用的 SIGNAL。
    static void qemu_tcg_init_cpu_signals(void)
    {
        sigset_t set;
        struct sigaction sigact;
     
        memset(&sigact, 0, sizeof(sigact));
        sigact.sa_handler = cpu_signal;
        sigaction(SIG_IPI, &sigact, NULL);
     
        sigemptyset(&set);
        sigaddset(&set, SIG_IPI);
        pthread_sigmask(SIG_UNBLOCK, &set, NULL);
    }
  2. cpu_signal 會將執行從 code cache 拉出來,回到 QEMU 處理 singal。
    static void cpu_signal(int sig)
    {
        if (cpu_single_env) {
            cpu_exit(cpu_single_env);
        }
        exit_request = 1;
    }

分層

依照 A Survey on Virtualization Technologies,虛擬化可分成如下幾層 (由上至下):

A hardware abstraction layer (HAL) is an abstraction layer, implemented in software, between the physical hardware of a computer and the software that runs on that computer. Its function is to hide differences in hardware from most of the operating system kernel, so that most of the kernel-mode code does not need to be changed to run on systems with different hardware.

An instruction set, or instruction set architecture (ISA), is the part of the computer architecture related to programming, including the native data types, instructions, registers, addressing modes, memory architecture, interrupt and exception handling, and external I/O. An ISA includes a specification of the set of opcodes (machine language), and the native commands implemented by a particular processor.

Linux® emulation in FreeBSD

  1. 透過設置不同的向量表,FreeBSD 可以同時運行 FreeBSD 和 Linux 執行檔。i386/i386/elf_machdep.ci386/linux/linux_sysvec.c 分別定義不同的向量表。
    struct sysentvec elf32_freebsd_sysvec = {
      .sv_size  = SYS_MAXSYSCALL,
      .sv_table = sysent,
      .sv_mask  = 0,
      .sv_sigsize = 0,
      .sv_sigtbl  = NULL,
      .sv_errsize = 0,
      .sv_errtbl  = NULL,
      .sv_transtrap = NULL,
      .sv_fixup = __elfN(freebsd_fixup),
      .sv_sendsig = sendsig,
      .sv_sigcode = sigcode,
      .sv_szsigcode = &szsigcode,
      .sv_prepsyscall = NULL,
      .sv_name  = "FreeBSD ELF32",
      .sv_coredump  = __elfN(coredump),
      .sv_imgact_try  = NULL,
      .sv_minsigstksz = MINSIGSTKSZ,
      .sv_pagesize  = PAGE_SIZE,
      .sv_minuser = VM_MIN_ADDRESS,
      .sv_maxuser = VM_MAXUSER_ADDRESS,
      .sv_usrstack  = USRSTACK,
      .sv_psstrings = PS_STRINGS,
      .sv_stackprot = VM_PROT_ALL,
      .sv_copyout_strings = exec_copyout_strings,
      .sv_setregs = exec_setregs,
      .sv_fixlimit  = NULL,
      .sv_maxssiz = NULL,
      .sv_flags = SV_ABI_FREEBSD | SV_IA32 | SV_ILP32,
      .sv_set_syscall_retval = cpu_set_syscall_retval,
      .sv_fetch_syscall_args = cpu_fetch_syscall_args,
      .sv_syscallnames = syscallnames,
    };
     
    struct sysentvec elf_linux_sysvec = {
      .sv_size  = LINUX_SYS_MAXSYSCALL,
      .sv_table = linux_sysent,
      .sv_mask  = 0,
      .sv_sigsize = LINUX_SIGTBLSZ,
      .sv_sigtbl  = bsd_to_linux_signal,
      .sv_errsize = ELAST + 1,
      .sv_errtbl  = bsd_to_linux_errno,
      .sv_transtrap = translate_traps,
      .sv_fixup = elf_linux_fixup,
      .sv_sendsig = linux_sendsig,
      .sv_sigcode = linux_sigcode,
      .sv_szsigcode = &linux_szsigcode,
      .sv_prepsyscall = NULL,
      .sv_name  = "Linux ELF",
      .sv_coredump  = elf32_coredump,
      .sv_imgact_try  = exec_linux_imgact_try,
      .sv_minsigstksz = LINUX_MINSIGSTKSZ,
      .sv_pagesize  = PAGE_SIZE,
      .sv_minuser = VM_MIN_ADDRESS,
      .sv_maxuser = VM_MAXUSER_ADDRESS,
      .sv_usrstack  = USRSTACK,
      .sv_psstrings = PS_STRINGS,
      .sv_stackprot = VM_PROT_ALL,
      .sv_copyout_strings = linux_copyout_strings,
      .sv_setregs = exec_linux_setregs,
      .sv_fixlimit  = NULL,
      .sv_maxssiz = NULL,
      .sv_flags = SV_ABI_LINUX | SV_IA32 | SV_ILP32,
      .sv_set_syscall_retval = cpu_set_syscall_retval,
      .sv_fetch_syscall_args = linux_fetch_syscall_args,
      .sv_syscallnames = NULL,
    };
  2. proc.h 裡面定義的 proc 資料結構內存該進程應該使用哪一個 sysentvec
    struct proc {
     
      struct sysentvec *p_sysent; /* (b) Syscall dispatch info. */
     
    };

術語

論文

文章

外部連結