看板 ASM 關於我們 聯絡資訊
曾經在這篇 ( 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 -- 凡經我手, 必屬佳作。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 58.114.140.223 ※ 文章網址: http://www.ptt.cc/bbs/ASM/M.1413561837.A.B7E.html ※ 編輯: descent (58.114.140.223), 10/18/2014 00:11:11