x86 process switch implementation (0)

作者: descent (「雄辯是銀,沉默是金」)   2014-10-18 00:03:41
曾經在這篇 ( http://goo.gl/mgVNVM )提到 os porcess 的切換, 那時候令我興奮不已
。但當時的概念還是很模糊, 我決定要用我自己的話來說明這個概念, 也證明我真的搞懂

化繁為簡是我的學習原則, 對於 process 切換的掌握度還不夠, 打算用自己的方法來實
作一個 process 切換的程式。希望這程式符合幾點:
程式碼小
在 dos 下執行
使用 x86 real mode
上述這些條件都是為了簡單, 若能用小小的程式以及簡單的執行環境就可以完成
process 切換, 相信理解起來會容易些。在 dos 使用 .com 執行檔, 可以讓我的程式最
大有 64 K 那麼大 (那麼小), 對於所有程式碼都是自己打造的來說, 已經非常夠用,
boot 磁區那 512 byte 才真的不夠人用。
那這程式有多小呢?大概像下面這樣:
simple_proc.S
1 #define STACK_FRAME_OFFSET 6
2 .code16
3 .text
4 .global begin
5 begin:
6 xchg %bx, %bx #bochs magic break point
7 cli
8
9 xor %eax, %eax
10 mov %cs,%ax
11 mov %ax,%ds
12
13 ## reset 0x30 interrupt
14 movw $0x0, %bx
15 movw %bx, %es
16 movw $switch_proc, %es:0xc0 # isr offset
17 movw %ax, %es:0xc2 #isr seg
18
19
20 movw $0xb800, %ax
21 movw %ax, %gs
22
23 ## set stack frame eip
24 movw $proc_a, stack_frame
25 movw $proc_b, stack_frame+STACK_FRAME_OFFSET
26
27 ## set stack frame cs
28 movw %cs, %ax
29 movw %ax, stack_frame+2
30 movw %ax, stack_frame+STACK_FRAME_OFFSET+2
31
32 ## set stack frame flag
33 # get flag
34 pushf
35 movw (%esp), %ax
36 popf
37 movw %ax, stack_frame+4
38 movw %ax, stack_frame+STACK_FRAME_OFFSET+4
39
40 int $0x30
41
42 mov $0x4c00, %ax
43 int $0x21
44
45 cur_proc:
46 .word 0x0
51
52 .space 256, 0
53 proc_stack_top_a:
54 .space 256, 0
55 proc_stack_top_b:
56
57 stack_frame:
58 .word 0x0# eip
59 .word 0x1# cs
60 .word 0x2# flag
61
62 .word 0x0# eip
63 .word 0x1# cs
64 .word 0x2# flag
65
66 .global proc_a
67 proc_a:
68 1:
69 mov $0x1, %ax
70 int $0x30
71 jmp 1b
72
73 .global proc_b
74 proc_b:
75 1:
76 mov $0x2, %bl
77 int $0x30
78 jmp 1b
79
80 .global switch_proc
81 switch_proc:
82 movw cur_proc, %dx
83 cmp $stack_frame, %dx
84 je 1f
85 movw $stack_frame, cur_proc
86 jmp 2f
87 1:
88 movw $stack_frame+STACK_FRAME_OFFSET, cur_proc
89 2:
90 movw cur_proc, %sp
91 iret
66 .global proc_a
67 proc_a:
68 1:
69 mov $0x1, %ax
70 int $0x30
71 jmp 1b
72
73 .global proc_b
74 proc_b:
75 1:
76 mov $0x2, %bl
77 int $0x30
78 jmp 1b
proc_a 和 proc_b 便是我們的兩個 process, 你可能會抗議那明明就只是兩個 function
。是的, 你沒說錯, 但你寫的 c main 程式是不是也只是個 function, 而你卻認為他是
一個 process 呢?若使用 call proc_a, call proc_b, 那是 function 的用法, 不是
process, 所以接下來的程式碼要用很其特的方法 (iret) 來執行 proc_a, proc_b。
這兩個 process 只有 3 行, 夠簡單, 只要略懂組合語言的程式員, 幾乎不用說明就可看
懂。使用的 process 切換方式是類似 windows 3.1 的 non-preemptive 方式, 需要由
process 自己釋放 cpu 來讓其他 process 執行。int $0x30 就是用來做這件事情。當然
我可以把 int $0x30 包裝成類似 os_yield(), 不過這樣複雜度就提高了。
( http://goo.gl/trgST8 )
上圖可以說明一切, 也許你會嫌字很醜, 應該用電腦畫才是, 不過手工可是很難得的。重
點在 stack_frame, 裡頭有 3 個欄位: 分別代表 eip, cs, flag, 用來儲存 proc_a,
proc_b 目前的這 3 個值。int 0x30 isr 便是切換 stack_frame, stack_frame+6 來執
行 proc_a, proc_b。
節錄 ref 1 的 int 指令做的事情:
把旗標暫存器 push 堆疊
禁止其他中斷發生
清除陷阱旗標
把 CS 暫存器 push 堆疊
把 INT n 的下一指令位址 push 堆疊
由 0000:(4n) 位址取出中斷服務程式所在位址,並執行長程跳躍指令,至該處繼續執行
節錄 ref 1 的 iret 指令做的事情:
由堆疊中 pop 4 bytes (cs:ip),並把控制權交到該 4 bytes 所指位址
由堆疊 pop 旗標暫存器 (2 bytes)
所以就是這樣來讓 proc_a, proc_b 可以輪流執行。先把 proc_a, %cs 的值填到
stack_frame eip, cs 的地方 (stack_frame, stack_frame+2), 將 %sp 指到
stack_frame 或是 stack_frame+6 的地方, 然後發動 iret 即可跳到 proc_a 或是
proc_b。因為 iret 會把 stack_frame, stack_frame+2 載入到 %eip, %cs, 而 cpu 會
執行 %cs:%eip 指向的程式碼, 就會去執行 proc_a, 這就是和直接 call proc_a 不同的
執行方式。
int 指令則會把下一個指令的 %cs:%eip 存到 %ss:%esp 指到的地方, 所以 int 發動的
時候, 會把 proc_a 下個指令存到 stack_frame, stack_frame+2 裡頭, 等著我們下次發
動 iret 再讓 proc_a 執行起來; 執行 proc_b 也是同樣的道理, 很容易理解吧!
這程式的執行結果不重要, 重要的是執行過程, 怎麼感受這個執行過程? 我是用 bochs
內建 debugger single step, 觀察所有暫存器, stack 來檢查程式是否有真的執行切換

6 xchg %bx, %bx #bochs magic break point
是 bochs magic break point, 程式執行到這行, 會讓 bochs 中斷停下來, 就可使用
single step指令來觀察整個程式行為。
而程式的過程便是在 proc_a, proc_b 之前相互執行, 為了簡單, 我沒有印出任何字元,
所以從螢幕上看不出任何事情, 為了有趣, 我自己倒是寫了一個印出 a, b 的版本, 有興
趣的朋友可以自己改看看, 在 proc_a 印出 a, prob_b 印出 b。
這個程式該怎麼執行呢? makefile 規則會把這隻程式編譯成 .com, 直接 copy 到 dos
執行即可, 再使用 bochs 的內建除錯器就可以追蹤整個流程。dos 環境最好不要載入任
何記憶體管理程式, ex: himem.sys, emm386.exe, 在我測試改寫 0x30 中斷時, 會造成
一些問題, 我花了不少時間排除這問題。
程式很簡單, 說明也很簡單, 希望不要造成誤會, 如果你已經理解這篇的解釋, process
switch 並沒有這麼簡單, 我簡化很多東西, 這沒有考慮很週嚴 (ex: 沒有保存所有的暫
存器), 真正的 process switch 還要加上不少 code, 而且我還沒搞定 x86 real mode
如何保存 %esp 的問題。x86 保護模式在權限切換時, iret/int 指令會保存 %ss:%esp。
這程式若用上保護模式, 那得加上不少 code, 模糊了我要表達的事情, 就先這樣。
儘管有如此缺失, 但用來作為 process switch 的實作理解, 不到 100 行的組合語言程
式能發揮如此功用, 已經足夠。
下篇文章 x86 process switch implementation (1) ( http://goo.gl/A7n5ig ) 就會複
雜一點了。
soure code:
https://github.com/descent/process ( http://goo.gl/IflJmV )
git commit : d25cb21e036b953f19ec69610c411c550dcfa8d6
x86 中斷改寫參考資料:
第 36 章 中斷 ( http://goo.gl/ZtwD1T )
中矢量表的构 ( http://goo.gl/ITR76N )
中服程序 ( http://goo.gl/gKGIzS )
f=false ( http://goo.gl/36DfaE )
channel=fflb ( http://goo.gl/H3JeLD )
// 本文使用 Blog2BBS 自動將Blog文章轉成縮址的BBS純文字 http://goo.gl/TZ4E17 //
blog 原文
http://descent-incoming.blogspot.tw/2013/03/x86-process-switch-implementation-0-in.html

Links booklink

Contact Us: admin [ a t ] ucptt.com