劉正仁
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    2
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # RISCV with TinyEMU contributed by < `johnnylord` > < `yiwei` > ## 預期目標 - 熟悉 GNU Toolchain 相關開發工具 - 接觸 RISC-V 處理器架構 - 客製化 Buildroot - 學習系統模擬器的內部運作機制 ## 檢查清單 ### riscv-emu 原始程式碼中多次出現 [virtio](https://www.linux-kvm.org/page/Virtio),這樣的機制對於 host 和 guest 兩端有何作用?在閱讀 [Virtio: An I/O virtualization framework for Linux](https://www.ibm.com/developerworks/library/l-virtio/index.html) 一文後,對照原始程式碼,你發現什麼? 在探討 virtio 之前,需要先了解 virtualization 有兩種類型的虛擬化方案,一則是 full virtualization,另一則是 paravirtualization。 - full virtualization : guest OS 運行在 hypervisor 之上,每當 guest OS 欲執行存取硬體資源的指令 (例如 I/O), hypervisor 會捕獲這個 request ,並模擬出這些指令的行為,讓 guest OS 以為自己是直接存取硬體資源(由 hypervisor 負責 Device emulation )。在這種模式下,每次 I/O 操作的路徑比較長,效能不佳。另外, guest OS 不知自己其實是被虛擬化出來的,所以在模擬器上運行的 guest OS 不需要多做額外的修改。 - paravirtualization : guest OS 知道自己是運行在 hypervisor 之上,所以會與 hypervisor 合作,使得 Device emulation 更有效率。guest OS 與 hypervisor 合作的方式,便是修改 guest OS,使其包含 Para-drivers ,實做不同裝置的驅動程式,視為前端 ; 而 hypervisor 則是負責實做「對應裝置的功能模擬」的驅動程式 ,視為後端。然而,不同 hypervisor(Kernel-based Virtual Machine (KVM)、 lguest、User-mode Linux) 的 Device emulation 機制各不相同,為了有統一的標準化界面,virtio 提供一個各 hypervisor 都通用的前端接口 ,增加了跨平台間的程式碼可重用性,可以視為硬體的抽象概念。 > Figure1:full virtualization 與 paravirtualization 的運作環境。 ![](https://i.imgur.com/9uEnl2c.png) > Figure2:透過 virtio,guest OS 有統一的裝置驅動程式(Front-end drivers),而各 hypervisor 的 Back-end drivers 不須統一,只須實現 Front-end drivers 所需的對應行為即可。 ![](https://i.imgur.com/RJVLyVS.png) virtio 定義了 2 個層次來支持 guest-to-hypervisor 的溝通。第一層為 virtio ,它是一個連接 front-end drivers 和 back-end drivers 的 virtual queue interface。可見下圖 figure3. ![](https://i.imgur.com/RXmhhKu.png) ### 透過 `temu root-riscv64.cfg`, 我們在 RISCV/Linux 模擬環境中,可執行 gcc 並輸出對應的執行檔,而之後我們則執行 `riscv64-buildroot-linux-gnu-gcc`,這兩者有何不同? (提示: cross-compiler, 複習 [你所不知道的 C 語言: 編譯器和最佳化原理篇](https://hackmd.io/s/Hy72937Me) 由於我們實驗模擬的環境,CPU 架構為 riscv64,而我的電腦本身是 Intel x86_64 的電腦架構。在host 端用原先 `gcc` 編譯的程式碼,所產生的執行檔是給 x86_64 電腦架構執行,而 `riscv64-buildroot-linux-gnu-gcc` 則是一個 `cross-compiler`,我們先在 host 端使用這個 `cross-compiler` 編譯一個執行檔,這個執行檔可以執行在 `riscv64` 的平台上。 以下做的小小實驗,利用 cross-compiler 和 gcc 編譯一個小程式,看其編譯輸出 ```clike // test.c #include <stdio.h> int main() { printf("Hello World\n"); return 0; } ``` ```shell ## compile code using gcc gcc -o x86 test.c -static riscv64-buildroot-linux-gnu-gcc -o riscv test.c -static ``` 利用 `file` 指令觀察輸出檔 ```shell $ file riscv riscv: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, for GNU/Linux 4.15.0, with debug_info, not stripped $ file x86 x86: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=eecd167a919b2800d64854b2b105e382b968b73b, not stripped ``` 如果你用 `objdump -d` 將執行檔做反組譯,你會發現在原系統可以,但是 `riscv` 則不行。 ```shell $ objdump -d riscv riscv: file format elf64-little objdump: can't disassemble for architecture UNKNOWN! ``` 這個結果很直觀,由於反組譯是根據那個系統架構的指令集,所以在 x86 的環境下反組譯 riscv 的執行檔當然無法成功。 ### 在 Guest 端透過 `$ dmesg | grep 9pnet` 命令,我們可發現 `9P2000` 字樣,這和上述 VirtFS 有何關聯?請解釋運作原理並設計實驗 ### 在 [TinyEMU System Emulator by Fabrice Bellard](https://bellard.org/tinyemu/readme.txt) 提到 “Network block device”,你能否依據說明,嘗試讓 guest 端透過 host 存取到網際網路呢? `Tinyemu` 透過 TUN/TAP 界面,實做了讓 guest 透過 host 存去到網際網路的功能。 不過在講解 TUN/TAP 前,先了解一般情況電腦存取網路的方式 ![](https://i.imgur.com/4lZww9G.png) - Switch(Physical Ethernet) switch 裝置常整合到 modem 中,switch 負責網路封包傳輸中 Layer2 的部份,處理 Ethernet frame,決定將封包送到那一個 physical port; modem 負責網路封包傳輸中 Layer1 的部份,將封包送網外部網路(實現存取網際網路的意思) - NIC(Network interface card) 網路介面卡(NIC)透過 RJ4 Cable 連接到 switch。 而虛擬化的世界中,VM 存取網路的架構如下 ![](https://i.imgur.com/eTKlXGw.png) 淺灰色部份可以為 host 端主機,內部透過虛擬化(軟體實現) switch 和 NIC,可以讓 host 端裡面的 VM 透過 host 存取到網路。其中 Linux bridge 實現了 switch。 這裡終於可以進到 TUN/TAP 了。TUN/TAP 是一個 linux kernel 支援的 driver,讓 user space 的程式能夠存取 Layer2/3 的封包資訊。那為什麼需要它呢? 因為在實體的 NIC 接受到封包時,kernel 會處理 Ethernet frame 並將其從封包中捨去。如此一來,傳遞到虛擬的 NIC 時,得到的封包就不完整。而 TUN/TAP 的功用就是告訴 Linux bridge(存在於 kernel space 中)將 Ethernet frame 的資訊保留並直接傳遞給虛擬的 NIC。 所以根據 `Tinyemu` 專案下的 `netinit.sh` 虛擬網路建構腳本 ```bash # host network interface connected to Internet (change it) internet_ifname="enx00e04c68197a" # setup bridge interface ip link add br0 type bridge # create and add tap0 interface to bridge ip tuntap add dev tap0 mode tap user $USER 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 ``` 會建構好 linux bridge 並增加 TAP 類別的虛擬裝置。而 `Tinyemu` 也就可以和 `tap0` 溝通,傳輸/接收網路封包。在 `temu.c` 中可以看到模擬器和 `tap0` 裝置連結。 ```clike #if !defined(_WIN32) && !defined(__APPLE__) if (!strcmp(p->tab_eth[i].driver, "tap")) { p->tab_eth[i].net = tun_open(p->tab_eth[i].ifname); if (!p->tab_eth[i].net) exit(1); } else #endif { fprintf(stderr, "Unsupported network driver '%s'\n", p->tab_eth[i].driver); exit(1); } ``` 最後根據 [Tinyemu's README](https://bellard.org/tinyemu/readme.txt),執行完 `netinit.sh` 後,啟動模擬器,並在模擬器中執行 ```bash ifconfig eth0 192.168.3.2 route add -net 0.0.0.0 gw 192.168.3.1 eth0 ``` 就可以存取網路了 ![](https://i.imgur.com/mBFrxt3.png) ### 最初實驗輸入 `$ temu https://bellard.org/jslinux/buildroot-riscv64.cfg`,然後就能載入 RISC-V/Linux 系統,背後的原理是什麼呢?請以 VirtIO 9P 檔案系統和 riscv-emu 對應的原始程式碼來解說 要啟動一個系統最起碼需要以下元件(不考慮類 MCU 等系統) - Bootloader - Kernel - Root file system 透過觀察 `Tinyemu` 提供的相關設定檔(`*.cfg`) ``` /* VM configuration file */ { version: 1, machine: "riscv64", memory_size: 128, bios: "bbl64.bin", kernel: "kernel-riscv64.bin", cmdline: "console=hvc0 root=/dev/vda rw", drive0: { file: "root-riscv64.bin" }, /* Also access to the /tmp directory. Use mount -t 9p /dev/root /mnt to access it. */ fs0: { tag: "/dev/root", file: "/tmp" }, eth0: { driver: "tap", ifname: "tap0" }, } ``` 不難發現 `bios`, `kernel`, `drive0` 就分別指定了這些必須的要件。不過現在設定檔是來自網路 `https://.../buildroot-riscv64.cfg`。 模擬器透過網路載入設定檔的流程大致如下 - ==`temu.c:virt_machine_load_config_file`== 建構傳遞在後續函式間的 logging 資料結構(`VMConfigLoadState`)開始執行載入動作 - ==`machine.c:config_load_file`== 循序的呼叫載入的相關函式並註冊 Callback 函式 - ==`fs_wget.c:fs_wget`== `curl` 下載任務的建立 - ==`fs_wget.c:fs_wget2`== 將 `curl` 下載任務加入任務工作池中 - ==`temu.c:fs_net_event_loop`== - ==`fs_wget.c:fs_net_set_fdset`== 從 `curl` 任務池中選出任務,並坐下載。 在上面的流程中,程式邏輯中出現大量的 Callback 技巧。以下是在 `Tinyemu` 中 callback 技巧的整理 1. 當某項任務的執行(如載入模擬器設定檔)跨越多個函式,或跨越一段時間,通常會建構一個 wrapper class,將傳遞函式之間的資料打包成一個單元。 > 如 `VMConfigLoadState`,當 virtual machine 在載入設定檔時,過程中會執行一連串的 Callback 函式,而在個個函式中,virtual machine 載入的狀態都紀錄在 `VMConfigLoadState` 中,並傳遞在函式之間。 2. Callback 界面的 convention。`callback(void *opaque)`,`func(..., callback, opaque)` 每當一個 `curl` 下載結束時,都會執行 `config_file_load_cb` Callback 函式。 ```cpp /* * opaque -> VMConfigLoadState 代表 VM 當前載入狀態 * err -> curl 任務是否順利完成 * data -> 下載的資料內容 * size -> 下載的資料大小 */ static void config_load_file_cb(void *opaque, int err, void *data, size_t size) { VMConfigLoadState *s = opaque; if (err < 0) { vm_error("Error %d while loading file\n", -err); exit(1); } s->file_load_cb(s->file_load_opaque, data, size); } ``` `config_file_load_cb` 又會繼續執行其他 Callback `config_file_loaded` 做下載的檔案內容解析。 ```cpp static void config_file_loaded(void *opaque, uint8_t *buf, int buf_len) { VMConfigLoadState *s = opaque; VirtMachineParams *p = s->vm_params; if (virt_machine_parse_config(p, (char *)buf, buf_len) < 0) exit(1); /* load the additional files */ /* such as root file system */ s->file_index = 0; config_additional_file_load(s); } ``` 解析的內容為以下 ```json /* VM configuration file */ { version: 1, machine: "riscv64", memory_size: 256, bios: "bbl64.bin", kernel: "kernel-riscv64.bin", cmdline: "loglevel=3 swiotlb=1 console=hvc0 root=root rootfstype=9p rootflags=trans=virtio ro TZ=${TZ}", fs0: { file: "https://vfsync.org/u/os/buildroot-riscv64" }, eth0: { driver: "user" }, } ``` 可以看到 ==bios==, ==kernel== 都指定了相關的檔案(指定在 host 端),而 ==fs0:file== 則是 https url,指定遠端的 file system。所以之後還要再下載這個檔案。 在解析上面的設定檔中,就會先做初步的 VM 初始化,透過 `machine` 屬性,得知模擬 `riscv64` 架構的虛擬機器。由於 `Tinyemu` 可以模擬都個機器,它定義了統一的 VM 界面。 ```cpp struct VirtMachineClass { const char *machine_names; void (*virt_machine_set_defaults)(VirtMachineParams *p); VirtMachine *(*virt_machine_init)(const VirtMachineParams *p); void (*virt_machine_end)(VirtMachine *s); int (*virt_machine_get_sleep_duration)(VirtMachine *s, int delay); void (*virt_machine_interp)(VirtMachine *s, int max_exec_cycle); bool (*vm_mouse_is_absolute)(VirtMachine *s); void (*vm_send_mouse_event)(VirtMachine *s1, int dx, int dy, int dz, unsigned int buttons); void (*vm_send_key_event)(VirtMachine *s1, bool is_down, uint16_t key_code); }; ``` 而不同的機器要實做背後的邏輯,如 `riscv_machine_class` ```cpp const VirtMachineClass riscv_machine_class = { "riscv32,riscv64,riscv128", riscv_machine_set_defaults, riscv_machine_init, riscv_machine_end, riscv_machine_get_sleep_duration, riscv_machine_interp, riscv_vm_mouse_is_absolute, riscv_vm_send_mouse_event, riscv_vm_send_key_event, }; ``` 以上大概是設定檔下載的流程,不過 root file system 還沒建立完成,以下開始探討 `Tinyemu` 如何建立 P9 file sytem。 --- 當執行到 ==temu.c:fs_net_init==,會繼續建構 P9 file system。由於細節太多,描述全部容易失焦,以下就大略描述建構的過程。 大部分檔案系統不管是 ext2, ext3, P9,它們都要管理整個檔案系統中的檔案,而檔案的資訊就是 **inode**。所以第一步驟就是建立 root file system 中背後所有 inode 的資訊。 過程中會再下載額外關於 root file system 的資訊。 1. 整體 root file system 的資訊 https://vfsync.org/u/os/buildroot-riscv64/head?nocache=1 ``` Version: 1 Revision: 3 NextFileID: 1fa3 FSFileCount: 8092 FSSize: 209223680 FSMaxSize: 1073741824 Key: RootID: 1fa2 ``` 2. root file system 架構 https://vfsync.org/u/os/buildroot-riscv64/files/0000000000001fa2 ``` Version: 1 040755 0 0 1536506432.658617668 etc 100600 0 0 243 1536499328 shadow 2 100644 0 0 116 1536499330 os-release 3 040755 0 0 1536499329 init.d 100755 0 0 423 1534684844 rcK 4 100755 0 0 408 1534684844 rcS 5 100755 0 0 359 1534684844 S40network 6 100755 0 0 649 1534680003 S01logging 7 100755 0 0 1354 1534680169 S50dropbear 8 100755 0 0 1630 1536492638 S10udev 9 100755 0 0 1321 1534684844 S20urandom a . ... ``` 由於檔案過大我就不列出全部,可以看到它提供我們關於遠端系統中所有檔案的資訊,權限,型態,時間,等等。這邊大概解釋一下如何解讀內容 ![](https://i.imgur.com/rAUJXH7.png) 有了這兩個檔案,就可以從原本空空的檔案系統 ![](https://i.imgur.com/7QXghyn.png) 慢慢建構出完整的檔案系統 ![](https://i.imgur.com/G0nnkoX.png) 而 P9 file system 操作的實做也和 VM 差不多,由於 `Tinyemu` 也預期支援其他 file system type,所以它也訂了ㄧ個統一的界面 ```cpp // fs.h struct FSDevice { void (*fs_end)(FSDevice *s); void (*fs_delete)(FSDevice *s, FSFile *f); void (*fs_statfs)(FSDevice *fs, FSStatFS *st); int (*fs_attach)(FSDevice *fs, FSFile **pf, FSQID *qid, uint32_t uid, const char *uname, const char *aname); // ... int (*fs_readlink)(FSDevice *fs, char *buf, int buf_size, FSFile *f); int (*fs_renameat)(FSDevice *fs, FSFile *f, const char *name, FSFile *new_f, const char *new_name); int (*fs_unlinkat)(FSDevice *fs, FSFile *f, const char *name); int (*fs_lock)(FSDevice *fs, FSFile *f, const FSLock *lock); int (*fs_getlock)(FSDevice *fs, FSFile *f, FSLock *lock); }; ``` 而 P9 file system 就是要實現這些操作,這些操作定義都在 `fs_net.c` 檔案中。 所以經過了一連串的載入和初始化 ==bootloader==, ==kernel==, ==root file system== 都完備了,也就可以進入系統啟動的階段了。 ### riscv-emu 內建浮點運算模擬器,使用到 [SoftFP Library](https://bellard.org/softfp/),請以 `sqrt` 為例,解說 `sqrt_sf32`, `sqrt_sf64`, `sqrt_sf128` 的運作機制,以及如何對應到 RISC-V CPU 模擬器中 ### 在 `root-riscv64.cfg` 設定檔中,有 `bios: "bbl64.bin"` 描述,這用意為何?提示:參閱 [Booting a RISC-V Linux Kernel](https://www.sifive.com/blog/all-aboard-part-6-booting-a-risc-v-linux-kernel) 根據提供的參考,`bbl` 全名為 Berkerly boot loader。它的運作如下 > ==bbl is expected to have been chain loaded from another boot loader==, with the entry point running in machine mode. ==It is passed a device tree from the prior boot loader stage==, and performs the following steps: > - ==The device tree that was passed in from the previous stage is read and filtered==. This allows bbl to strip out information that Linux shouldn't be interested in. > - ==`bbl` jumps to the start of its payload==, which in this case is Linux. 上面的重點已經大致標出,不過還是可以做個總結 `bbl64.bin` 作為 bootloader,會先將 device tree 的內容做過濾,將 linux kernel 載入記憶體並轉交控制權給 kernel。 ### 能否用 `buildroot` 編譯 Linux 核心呢?請務必參閱 [Buildroot Manual](https://buildroot.org/downloads/manual/manual.html) `buildroot` 有提供選項讓我們選擇是否編譯 linux kernel。進到設定畫面中的 **Kernel** 選項中 ![](https://i.imgur.com/0Rwlh8T.png) 若選定 `Linux Kernel` 選項,則可以進一步的做設定,如指定 kernel 的版本,從哪裡取的 kernel 原始庫,kernel dot-config 檔案等等。 ### 核心啟動的參數 `console=hvc0 root=/dev/vda rw` 代表什麼意思呢?這對應到模擬器內部設計的哪些部分? 核心啟動參數(kernel command line)常被用在讓開發者傳遞一些訊息給 kernel,在系統初始化期間做一些適當的客製化。可傳遞給 kernel 的參數很多,可以查閱 kernel 專案下的 `Documentation/admin-guide/kernel-parameters.txt` 得到每個參數的說明。 查閱上述文件得知 `console=hvc0 root=/dev/vda rw` | parameter | value | Description | | --- | --- | --- | | console | hvc0 | 指定 hypervisor console device 為系統的 output console device | | root | /dev/vda | 指定 /dev/vda 為 root file system | | rw | | 以可讀可寫的權限掛載 root file system | 對應到 `tinyemu` 系統內部,kernel command line 是寫在 Device tree 中,而 kernel 透過由 bootloader 所傳遞 Device tree 在 memory 中的位址,就可以去解析 device tree 的內容。 以下看在 `tinyemu` 中模擬 `riscv64` 機器的 Device tree 內容。 ```clike /* riscv_machine.c */ static int riscv_build_fdt(RISCVMachine *m, uint8_t *dst, uint64_t kernel_start, uint64_t kernel_size, const char *cmd_line) { FDTState *s; /* ... */ s = fdt_init(); /* ... */ fdt_begin_node(s, "chosen"); fdt_prop_str(s, "bootargs", cmd_line ? cmd_line : ""); if (kernel_size > 0) { fdt_prop_tab_u64(s, "riscv,kernel-start", kernel_start); fdt_prop_tab_u64(s, "riscv,kernel-end", kernel_start + kernel_size); } fdt_end_node(s); /* chosen */ fdt_end_node(s); /* / */ size = fdt_output(s, dst); /* ... */ fdt_end(s); return size; } ``` 上面看到,如果有定義 `cmd_line`,則會將 `cmd_line` 的內容存放在 `chosen` node 裡面的 `bootargs` property 裡面。 根據 [Device tree specification](https://www.devicetree.org/downloads/devicetree-specification-v0.1-20160524.pdf) 所定義的內容 ![](https://i.imgur.com/xIPCVw2.png) `bootargs` 的定義如下 > ==A string that specifies the boot arguments for the client program==. The value could potentially be a null string if no boot arguments are required. ### 為何需要在 host 端準備 `e2fsprogs` 工具呢?具體作用為何呢? 根據 Wiki 上的解釋,`e2fsprogs` 是關於操作管理 `ext2`, `ext3`, `ext4` 檔案系統的工具程式 > ==e2fsprogs (sometimes called the e2fs programs) is a set of utilities for maintaining the ext2, ext3 and ext4 file systems.== Since those file systems are often the default for Linux distributions, it is commonly considered to be essential software. 且查看 `e2fsprogs` 在 buildroot 中存放的位置。 ![](https://i.imgur.com/97Eon0t.png) 可以看到它放在 `build/busybox-1.30.1` 目錄下,所以 `e2fsprogs` 是被納入 `busybox` 工具程式中。 ### root file system 在 Linux 核心的存在意義為何?而 initramfs 的存在的考量為何? 系統的初始化和 root file system 有很大的關係。kernel 初始化的最後部分,會在 root file system 中找尋相關的 init 程式,所以以下這段程式碼被執行前,root file system 就已經要被掛載,且 init 相關程式要放在對的位置。 ```clike= /* init/main.c */ static int __ref kernel_init(void *unused) { // ... if (execute_command) { ret = run_init_process(execute_command); if (!ret) return 0; panic("Requested init %s failed (error %d).", execute_command, ret); } if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0; panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/admin-guide/init.rst for guidance."); } ``` 上面提到這段程式碼被執行前,root file system 就要被掛載(是廢話,因為 init 程式在 root file system 上),不過在真正的 root file system 被掛載前,可能會有其他暫時的 root file system 被掛載並且用來做早期的系統啟動,而這暫時性的 root file system 叫做 Initial RAM disk。**其存在的原因大部分是為了提前載入某些驅動程式**。例如,在 initial ram disk 中先載入 Ext4 的驅動,之後在掛載真正的 root file system 才能掛載。實作 initial ram disk 概念的方法有`initrd`(舊方法) 和 `initramfs`(新方法)。 :::info 可以參閱 linux 專案下的 `Documentation/filesystems/ramfs-rootfs-initramfs.txt` 檔案得到關於 `initramfs` ::: ### `busybox` 這樣的工具有何作用?請搭配原始程式碼解說 (提示: 參見 [取得 GNU/Linux 行程的執行檔路徑](http://blog.linux.org.tw/~jserv/archives/002041.html)) Busybox 在嵌入式系統領域是一個很受歡迎的工具程式。Busybox 易於設定,編譯,和使用。它成功的將一些常用的 utility program 整合成一個執行檔(將一些常用程式碼片段與多個工具程式分享,達到避免重複的程式碼片段),大大縮小硬體空間需求。 Busybox 是一個模組化的專案,可以透過類似於 linux kernl 提供的輔助工具程式,設定並客製化我們自己的 Busybox。 以下假設一個系統使用 `busybox`,他的 root file system 如以下所示 ``` $ tree . |-- bin | |-- ash -> busybox | |-- busybox | |-- cat -> busybox | |-- cp -> busybox | |-- ... | '-- zcat -> busybox |-- linuxrc -> bin/busybox |-- sbin | |-- init -> ../bin/busybox ... ``` 大部份的 utility program 都是 symlink 且都指向 `/bin/busybox`。所以執行 `ls`, `cp`, `cat` 等等本質上都是在執行 busybox。然而 busybox 必須知道要執行哪個功能,而這項資訊就是來自 `argv[0]`。 因為我們知道 `argv` 儲存了 command line 的資訊。在執行程式時 `argv[0]` 多為程式的名稱。 ``` $ ls -l # argv[0] => ls $ cat file.txt # argv[0] => cat ``` busybox 根據 `argv[0]` 就可以知道要執行什麼樣的任務。 ###### tags: `sysprog2019`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully