# 2019q1 Homework4 (riscv) contributed by < `afcidk` > ## 自我檢查清單 ### Full virtualization v.s. Para-virtualization 在 full virtualization(全虛擬化)中,guest OS 直接跑在 hypervisor 之上,因此 guest 並不需要知道自己是否有被虛擬化。 但是在 para-virtualization(半虛擬化)中,guest OS 會知道自己跑在 hypervisor 上,而且也需要修改 guest OS 來使 guest-to-hypervisor 的 transition 更有效率。 ![](https://www.ibm.com/developerworks/library/l-virtio/figure1.gif) 上圖左是 full virtualization,另一個是 para-virtualization。可以發現在兩者的差異在 device emulation 上層的部份。Full virtualization 會模擬整個硬體,而 para-virtualization 則由 guest OS 和 hypervisor 共同模擬。 ### virtio virtio 是在 paravirtualized hypervisor 上的抽象層,他提供了一個抽象的介面(front-end)來簡化在不同平台間(e.g. KVM, lguest)的開發複雜度,因為我們只需要開發一種 front-end driver 就可以適用到不同的 hypervisor 平台上。 ![](https://www.ibm.com/developerworks/library/l-virtio/figure2.gif) ## Buildroot 是什麼 Buildroot 由一群 Makefile 和 patch 檔案組成,他讓我們可以自動化的生成一個 bootable Linux 環境,像是目標環境的 cross-compilation toolchain,還有 kernel image, bootloader 等... 在這次的作業中,我們就是使用 buildroot 在 RISC-V 架構上建構 root file system。編譯完後,可以在 `output/images` 底下發現建立好的 root file system。 ### 9p2000 與 VirtFS 的關聯 9pnet 代表的是 9p network protocol,是在貝爾實驗室的 Plan 9 計劃中被開發出來的網路協定。 > 第四版的修正後,9P 被改名叫作 9P2000 了。 [v9fs](https://www.kernel.org/doc/Documentation/filesystems/9p.txt) 是在 Unix 上的 Plan 9 remote filesystem protocol 實作。 VirtFS 指的是 **Plan 9 folder sharing over Virtio**。VirtFS 即是透過 v9fs 這個檔案系統來進行檔案目錄共享的。 ### 嘗試讓 guest 端透過 host 存取到網際網路 根據 Network usage 的說明,我們需要做一些設定才可以讓 guest 端存取到網路,先介紹一些必要的知識 * tap/tun tun (network TUNnel), tap (network TAP), 是虛擬的網路介面,他們不需要實際的硬體,利用純粹的軟體來代替硬體的功能。 tap 負責 data link layer 的模擬,而 tun 則是屬於 network layer。這邊我們可以簡單的解讀成 tap 就是虛擬的設備,tun 負責決定如何 routing。 * bridge bridge 是在 data link layer 中負責相連兩個不同的網路。在這次的 guest/host 例子中,bridge 的功能就是串聯 VM 和 host 的網路。 ![](https://i.imgur.com/DdOoZw1.png) * iptables, nat iptables 是 Linux 中一個 userspace 的軟體,他可以讓我們管理封包的處理方式。 nat 的全稱是 Network Address Translation。是一種在封包通過 router 或是防火牆時重寫 IP address 的一種技術。之所以需要 nat 是因為外網和內網的 IP address 是隔離開的,當 guest 想要向外連線時,我們需要 source IP address 在 nat 被重寫,以 host 的 IP address 當作 src IP 向外傳。 觀察 tinyemu 提供的 `netinit.sh` 腳本 ```shell internet_ifname="wlp3s0" # my interface is wlp3s0 # setup bridge interface ip link add br0 type bridge # create and add tap0 interface to bridge ip tuntap add dev tap0 mode tap user afcidk # replace with username ip link set tap0 master br0 ip link set dev br0 up ip link set dev tap0 up ifconfig br0 192.168.3.1 # setup NAT to access to Internet echo 1 > /proc/sys/net/ipv4/ip_forward # delete forwarding reject rule if present #iptables -D FORWARD 1 iptables -t nat -A POSTROUTING -o $internet_ifname -j MASQUERADE ``` 這個腳本幫我們建立一個 bridge 和 tap device, 並在 nat table 設定 POSTROUTING ![](https://i.imgur.com/iP5I2VM.png) 我們還需要更改設定檔 ``` eth0: { driver: "tap", ifname: "tap0" } ``` 這段指的是我們使用 tap driver, 對應到的 interface 名稱是 tap0,這個 interface 在 temu 中會叫作 eth0。 另外,在啟動 temu 之後,我們需要手動給 eth0 一個 IP address,並把 default gateway 改為前面設定好的 bridge IP。 ``` ifconfig eth0 192.168.3.2 route add -net 0.0.0.0 gw 192.168.3.1 eth0 ``` ### 核心啟動的參數 `console=hvc0 root=/dev/vda rw` 代表的意思 在設定檔 `root_9p_riscv64.cfg` 中可以看到 `cmdline: "console=hvc0 root=/dev/vda rw"`,這行是在設定啟動 kernel 時的參數。 在 [kernel-parameters](https://www.kernel.org/doc/Documentation/admin-guide/kernel-parameters.txt) 中可以找到相關的用法, `console=` 是用來指定輸出要使用的 console,`hvc<n>` 則是代表 hypervisor console device。 `root=` 用來指定 root filesystem,而 `rw` 代表我們把 filesystem mount 成可讀寫。 ### `$ cat /proc/loadavg` 的輸出意義 從 `man proc` 可以看到對 `/proc/loadavg` 的說明 :::info The first three fields in this file are load average figures giving the number of jobs in the run queue (state R) or waiting for disk I/O (state D) averaged over 1, 5, and 15 minutes. They are the same as the load average numbers given by uptime(1) and other programs. The fourth field consists of two numbers separated by a slash (/). The first of these is the number of currently runnable kernel scheduling entities (processes, threads). The value after the slash is the number of kernel scheduling entities that currently exist on the system. The fifth field is the PID of the process that was most recently created on the system. ::: 實際的 `cat /proc/loadavg` 會有類似下方的結果 ``` 0.67 0.48 1.03 1/722 30937 ``` 這5個欄位分別代表 * 一分鐘內平均 job 數量 * 五分鐘內平均 job 數量 * 十五分鐘內平均 job 數量 * 當前 running kernel scheduling entity/總共 running kernel scheduling entity 數量 * 最近產生的 process ID > running kernel scheduling entity 代表 scheduler 在做 scheduling 時的最小單位,也就是 thread (不是 process) 前 3 個欄位資訊也可以透過 `uptime` 指令看到。 這個資訊給了我們**平均**的數值,但是在 kernel space 進行浮點運算[並不是一件簡單的事](https://stackoverflow.com/questions/13886338/use-of-floating-point-in-the-linux-kernel),因此我們著手觀察 loadavg 的實作方式。 ## 編譯 kilo 編輯器 `buildroot-riscv-2018-10-20/output/host/usr/bin/` 底下有一些開發工具,像是在 RISC-V 架構下使用的 compiler `riscv64-buildroot-linux-gnu-gcc`,我們就是使用這個來編譯可以在 RISC-V 底下跑的 kilo 編輯器。 但是這樣每次都要額外再編譯他,因此我們可以修改 buildroot,順便把 kilo build 好放到 bin 底下。 [Buildroot manual](http://buildroot.uclibc.org/downloads/manual/manual.html#adding-packages) 有提到要如何加一個 package 到 buildroot 中。 * `package/Config.in` * `package/kilo` * `package/kilo/Config.in` * `package/kilo/kilo.mk`