K系列

建立 uCore OS 開發環境

一、前言

線上學習平台「学堂在线」有一門 Tsinghua University 的操作系统 (自主模式)課程,其中實驗所用到的 OS 就是 uCore,使用 vagrant 可以避免不必要的圖形介面操作,非常適合熱愛使用文字介面的開發者

二、以下是我的機器狀態

  1. OS/Kernel: 4.9.66-1-MANJARO
  2. Compiler: gcc version 7.2.0
  3. Vagrant: 2.0.1
  4. Virtualbox: 5.2.2 r119230

三、步驟

  1. 前住 https://github.com/chyyuu/ucore_os_lab,搜尋關鍵字「VirtualBox的虚拟硬盘文件压缩包2015版」,然後前住它的超連結下載 mooc-os-2015.vdi.xz,完成後請解壓它並得到 mooc-os-2015.vdi。
  2. 開啟 virtualbox,新增一個全新的 Ubuntu (64 bit) 虛擬機器,命名為 ubuntu_ucore 並指定它的 virtual hard disk file 是 mooc-os-2015.vdi 。
  3. 由於預設的 password 是空格, 在後續的過程中使用 vagrant 會出現非預期行為。因此,建議先使用 virtualbox 登入 ubuntu_ucore 修改預設密碼。
  4. $ vagrant package --base ubuntu_ucore --output ./ucore.box   (制作 box 格式的檔案,注意參數 --base 必須指定是剛才設定的虛擬機器名字,即是 ubuntu_ucore。確認無誤並送出指令,然後稍候片刻。)
  5. $ vagrant init    (初始化 Vagrantfile)
  6. $ vim Vagrantfile,在   Vagrant.configure("2") do |config| ... end   之中,加入以下三行的設定
    • config.vm.box = "ucore.box"
    • config.ssh.username = "moocos"
    • config.ssh.password = ""
  7. $ vagrant up
  8. $ vagrant ssh    (使用 ssh 登入虛擬機器)
  9. $ cd moocos/ucore_lab/labcodes/lab1/    (切換目錄)
  10. $ vim Makefile     (在 213 行加入 -nographic 設定,使得 SDL 沒有初始化)
    • $(V)$(QEMU) -nographic -parallel stdio -hda $< -serial null
  11. $ make && make qemu    (看到 uCore 輸出特定的信息)
  12. $ sudo shutdown -h now    (關閉虛擬機器)
  13. $ vagrant halt
廣告
K系列

開發環境︰seL4 + Genode

下載 Toolchain

瀏覽 Sourceforge,下載 15.05-x86_64 的版本。

安裝 Toolchain

$ cd ~/Download
sudo tar xPfj genode-toolchain-15.05-x86_64.tar.bz2

完成安裝

倘若成功安裝,執行以下的指令後就可以看到對應的輸出

$ ls /usr/local/genode-gcc
arm-none-eabi bin include lib libexec share x86_64-pc-elf


取得 Genode 的原始碼

$ cd ~/Workspaces
$ git clone https://github.com/genodelabs/genode

等待片刻,就能夠看到以下的輸出

$ cd genode
$ ls
doc LICENSE README repos tool VERSION


取得 seL4 的原始碼

目前的路徑

$ pwd
~/Workspaces/genode

新增一個空的 hash 檔案,然後更新它。

$ touch repos/base-sel4/ports/sel4.hash
$ ./tool/ports/update_hash sel4

正式下載 seL4 的原始碼

$ ./tool/ports/prepare_port sel4

等待片刻,就能夠看到新的目錄,名字是 contrib

$ ls
contrib doc LICENSE README repos tool VERSION

以下就是 seL4 的目錄下的內容

$ ls -al contrib/sel4-(hash-code)/src/kernel/sel4
total 132
drwxrwxr-x 8 gapry gapry 4096 1月 25 14:14 .
drwxrwxr-x 3 gapry gapry 4096 1月 25 14:14 ..
-rw-rw-r-- 1 gapry gapry 2428 1月 25 14:14 CAVEATS-generic.txt
-rw-rw-r-- 1 gapry gapry 756 1月 25 14:14 CAVEATS-ia32.txt
-rw-rw-r-- 1 gapry gapry 1393 1月 25 14:14 CONTRIBUTORS.md
-rw-rw-r-- 1 gapry gapry 22502 1月 25 14:14 gdb-macros
drwxrwxr-x 8 gapry gapry 4096 1月 25 14:14 .git
drwxrwxr-x 9 gapry gapry 4096 1月 25 14:14 include
-rw-rw-r-- 1 gapry gapry 13821 1月 25 14:14 Kconfig
drwxrwxr-x 6 gapry gapry 4096 1月 25 14:14 libsel4
-rw-rw-r-- 1 gapry gapry 1482 1月 25 14:14 LICENSE_BSD2.txt
-rw-rw-r-- 1 gapry gapry 15865 1月 25 14:14 LICENSE_GPLv2.txt
-rw-rw-r-- 1 gapry gapry 696 1月 25 14:14 .gitignore-rw-rw-r-- 1 gapry gapry 18400 1月 25 14:14 Makefile
drwxrwxr-x 5 gapry gapry 4096 1月 25 14:14 manual
-rw-rw-r-- 1 gapry gapry 2505 1月 25 14:14 README.md
drwxrwxr-x 10 gapry gapry 4096 1月 25 14:14 src
drwxrwxr-x 2 gapry gapry 4096 1月 25 14:14 tools


設定 seL4 的執行平臺

$ ./tool/create_builddir sel4_x86_32
Successfully created build directory at ~/Workspaces/genode/build/sel4_x86_32.
Please adjust ~/Workspaces/genode/build/sel4_x86_32/etc/build.conf according to your needs.


編譯和執行

$ cd build/sel4_x86_32
$ make run/test

倘若編譯成功,執行後就可以看到以下的輸出

int main(): set_ipc_buffer
int main(): seL4_SetUserData
int main(): seL4_Untyped_RetypeAtOffset (TCB) returned 0
int main(): seL4_Untyped_RetypeAtOffset (EP) returned 0
int main(): seL4_TCB_SetIPCBuffer returned 0
int main(): seL4_TCB_WriteRegisters returned 0
int main(): seL4_TCB_SetSpace returned 0
int main(): seL4_Call, delegating a TCB capability
void second_thread_entry(): call seL4_Wait
void second_thread_entry(): returned from seL4_Wait, call seL4_Reply
void second_thread_entry(): msg_info: got unwrapped 0
void second_thread_entry(): got extra caps 1
void second_thread_entry(): label 13
void second_thread_entry(): returned from seL4_Reply
void second_thread_entry(): call seL4_Wait
int main(): returned from seL4_Call
int main(): seL4_Call, delegating a TCB capability
void second_thread_entry(): returned from seL4_Wait, call seL4_Reply
void second_thread_entry(): msg_info: got unwrapped 0
void second_thread_entry(): got extra caps 1
void second_thread_entry(): label 13
int main(): returned from seL4_Call
int main(): seL4_Call, delegating a minted endpoint capability
int main(): seL4_CNode_Mint (EP_MINTED_CAP) returned 0
void second_thread_entry(): returned from seL4_Reply
void second_thread_entry(): call seL4_Wait
void second_thread_entry(): returned from seL4_Wait, call seL4_Reply
void second_thread_entry(): msg_info: got unwrapped 5
void second_thread_entry(): got extra caps 3
void second_thread_entry(): label 13
void second_thread_entry(): badge 111
int main(): returned from seL4_Call
int main(): seL4_Untyped_RetypeAtOffset (PAGE_TABLE) returned 0
int main(): seL4_Untyped_RetypeAtOffset (PAGE) returned 0
int main(): seL4_IA32_PageTable_Map returned 0
int main(): seL4_IA32_Page_Map to 0x40001000 returned 0
int main(): seL4_CNode_Copy returned 0
int main(): seL4_IA32_Page_Map to 0x40002000 returned 0
read from 0x40002000: "Data written to 0x40001000"
Caught cap fault in send phase at address 0x0
while trying to handle:
vm fault on data at address 0x1122 with status 0x6
in thread 0xe3ff8880 at address 0x100c60c
void second_thread_entry(): returned from seL4_Reply
void second_thread_entry(): call seL4_Wait


使用 GDB 除錯

$ cd ~/Workspaces/genode/build/sel4_x86_32

在第一個終端機執行以下的指令

$ gdb bin/test-sel4 -ex "target remote :1234"

在第二個終端機執行以下的指令

$qemu-system-i386 -nographic -m 64 -cdrom var/run/test.iso -s


Reference

[0] http://genode.org/download/tool-chain
[1] http://genode.org/documentation/articles/sel4_part_1
[2] http://genode.org/documentation/genode-foundations-15-05.pdf

C

閱讀 C 語言規格書的重要性之序章 : 一元運算元的案例分析

案例一

引述 C 語言規格書中的三點來解釋為何以下程式碼能正常運作

int main() {
   return (********puts)("Hello");
}

[1] 6.5.3.2-4 The unary * operator denotes indirection. If the operand pointers to a function, the result is a function designator;

[2] 6.3.2.1-4 A function designator is an expression that has function type.

[3] * is Right associative operator

由於 puts 的 function signature 是 int puts(const char *s),所以, 每一次經過 * operator 的運算後得到的結果是仍然是 int. 因此, * 的數目不會影響結果。最後 return 的值是根據 s 的長度加上 ‘\n’。而這個例子 return 給 entry point 的值是 6.


案例二

以下的表達式 [a] 和 [b] 是否等價?

[a] (**printf)("...");
[b] (&printf)("...");

引述 C語言規格書:

[0] 6.3.2.1-4 A function designator is an expression that has function type.
[1] 6.5.3.2-1 The operand of the unary &; operator shall be a function designator
[2] 6.5.3.2-3 The unary &; operator yields the address of its operand.
[3] 6.5.3.2-4 The unary * operator denotes indirection. If the operand points to a function, the result is a function designator.

假設 printf 的 function signature 是 int printf (const char *, …);
那麼每一次經過 * operator 和 & operator 的運算後得到的結果是仍然是 int ,所以這兩個 expression 是等價的

[a] (**printf)(“..") -> (* int printf)(“..") -> (int printf)(“..")
[b] (&printf)(“…"); -> int printf (“…");

如果把 (&printf)(“…"); 變成 (&&printf)(“…"); 那麼出現以下 Error Message:

error: called object is not a function or function pointer

原因是, 第一次的 & operator 的運算後得到的結果是 int; 第二次進行 & operator 的時候, semantics 是 address of operand, 而 operand 就是 int. 所以就會出現 Error.


案例三和四是以上的變化,其原理是一樣的。


案例三

double mm = 111.17;

double* get_money (double m) {
    mm = m;
    return &mm; 
}

int main() {
    double* m1 = (&get_money)(3.7117718);
    printf("%d\n", (&printf)("%.3lf\n", *m1));

    double* m2 = (*&*&get_money)(7.3881171);
    printf("%d\n", (&printf)("%.5lf\n", *m2));

    return 0;
}

/*
 * gcc -w -std=c99 -o main main.c && ./main 
 * 3.712
 * 6
 * 7.38812
 * 8
 */


案例四

 struct bank {
    double money;
    float rate;
};

char* ary = {'A', 'B', 'C'};

char* get_char(void) {
    return ary + 1;
}

struct bank* get_bank(struct bank *b) {                                                 
    b->money = 11973.711;
    b->rate = 817.3;
    return b;
}

double get_money (double m) {
    return m;  
}

int main() {
    struct bank* btw = (struct bank *) malloc (sizeof (struct bank));
    btw->money = 117.37911;
    btw->rate = 3.718;

    struct bank* bmo = (***************************get_bank)(btw);

    printf("%d\n",  (****puts)("Hello"));
    printf("%lf\n", (******************get_money)(bmo->money));                         
    printf("%c\n",  (***************************************************************************************************************************************************************************************************************************************************************************get_char)());
    
    return (**************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************puts)("Hello World!!");                  
}

/*
gcc -g -w -std=c99 -o main main.c
./main 
Hello
6
11973.711000
B
Hello World!!
*/

-完-

C

利用 Bit-Trick 實作分支陳述式

根據 C 語言規格書中 6.8.4 所定義的 Context-free grammar

selection-statement:
          if ( expression ) statement
          if ( expression ) statement else statement
          switch ( expression ) statement

倘若我們選擇了第一條 production rule,那麼 gcc 一定會產生 Branch Instructions。基於效能上的需求,可以使用以下實作。

假設存在兩個有號整數: int32_t a, b; 和分支陳述式 if (b < 0) a++; , 那麼分支陳述式可以取代為 a -= b >> 31;

一開始我們定義 b 是一個有號整數,因此 b 右移 31 位後,其值必為 Sign Bit 。也就是說,如果 b > 0, 那麼 b >> 31 必為 0。如果 b < 0, 那麼 b >> 31 必為 -1。a -= -1; -> a = a - (-1) -> a = a + 1; -> a++;

可以用 GDB 來觀察

7 a = 3; b = -5; c = 7; d = 11;
8 a -= b >> 31;
9 c -= d >> 31;

p/x b
$1 = 0xfffffffb
p/x d
$2 = 0xb
p/x b >> 31
$3 = 0xffffffff
p/x d >> 31
$4 = 0x0

-完-

C

白板上的程式碼

幾天以前我被要求在白板之上,使用不多於三分鐘的時間,寫出 Big Endian to Little Endian 和 Little Endian to Big Endian 的 C 語言程式碼。

要實作出這兩個函式,除了要知道 MSB 和 LSB 的 Byte Order 不一樣之外,還要考慮 Data 是 Unsigned, 還是 Signed ? 是 32 位元,還是 64 位元?

我以 32 位元作為例子分享給大家。

轉換函式
int32_t ltob(int32_t num) {
    return (num >> 24 & 0x000000FF) ^
            (num >> 8 & 0x0000FF00) ^
            (num << 8 & 0x00FF0000) ^
            (num << 24 & 0xFF000000);  
}
測試程式
void main() {
    int32_t l = -0x12345678;
    int32_t b = ltob(l);
    while (1);
}
使用 GDB 進行驗証

$ gcc -g -o test test.c

(gdb) p/x l
$1 = 0xedcba988
(gdb) p/x b
$2 = 0x88a9cbed

不出所料,在 GDB 中能看到預期的結果。接著,在 GDB 中觀察以下的 Byte Order 變化。

(gdb) p/x l
$5 = 0xedcba988
(gdb) p/x l >> 24
$6 = 0xffffffed
(gdb) p/x l >> 24 & 0x000000FF
$7 = 0xed

從上述的觀察可以得出結論︰Operator Shift 決定了新的 Byte Order,Operator And 消除多餘的 Byte。同樣的道理可以轉換其他的 Byte ,最後用 XOR 組合它們即可。


[註]︰可以利用 Objdump 觀察機器的 Endian

void main() {
    int32_t l = 0x12345678; 
}

$ gcc -o test test.c
$ objdump -d test


  400589:   55                      push   %rbp
  40058a:   48 89 e5                mov    %rsp,%rbp
  40058d:   c7 45 fc 78 56 34 12   movl   $0x12345678,-0x4(%rbp)
  400594:   b8 00 00 00 00          mov    $0x0,%eax
  400599:   5d                      pop    %rbp
  40059a:   c3                      retq   
  40059b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

– 完 –

Rust

建立 Rust 的開發環境

執行以下的指令,然後耐心等待。

curl -s https://static.rust-lang.org/rustup.sh | sudo sh

結束安裝後,執行rustc -V可以查看 Rust 的版本。

Reference︰Official WebSite Rust-lang