2012年3月24日 星期六

os process

實作難度: 難

blog 和書不同, 可以展示動態的畫面。下面的影片看來實在容易, 印出某些字串跑來跑去而已。不過其內涵卻不簡單。其中有一個 timer isr, 3 個 process, 透過每一次的 timer isr, 分別切換 process A, B, C 三個 process。這是從Orange's 一個作業系統的實現第六章的內容修改而來 (其實都馬是複製貼上 XD)。CPU 平台是 intel x86 系列。



process 台灣翻譯為行程, 在談論電腦的技術中, 多少會談論這個東西, 但是要能很精確理解什麼是一個 process, 恐怕只有實作過一個 process 才能真的理解。

簡單來說就是一段程式碼的執行環境。包含程式碼本身, cpu 暫存器, 某些記憶體狀態。聽來很簡單, 但在程式碼的實作上卻沒有想像中容易。

process.h
  1 #ifndef PROCESS_H
  2 #define PROCESS_H
  3
  4 #include "type.h"
  5 #include "protect.h"
  6
  7 #define LDT_SIZE 2
  8 #define NR_TASKS 3
  9
 10 #define STACK_SIZE_TESTA 0x8000
 11 #define TASK_STACK 0x8000
 12 #define STACK_SIZE_TOTAL (0x8000*NR_TASKS)
 13 #define SELECTOR_LDT_FIRST (INDEX_LDT_FIRST*8)
 14
 15 #define PRIVILEGE_TASK 1
 16
 17 #define RPL_TASK 1
 18
 19
 20 typedef struct StackFrame_
 21 {
 22   u32 gs;
 23   u32 fs;
 24   u32 es;
 25   u32 ds;
 26   u32 edi;
 27   u32 esi;
 28   u32 ebp;
 29   u32 kernel_esp;
 30   u32 ebx;
 31   u32 edx;
 32   u32 ecx;
 33   u32 eax;
 34   u32 retaddr;
 35   u32 eip;
 36   u32 cs;
 37   u32 eflags;
 38   u32 esp;
 39   u32 ss;
 40 }StackFrame;
 41
 42 typedef struct Process_
 43 {
 44   StackFrame regs;
 45   u16 ldt_sel;
 46   Descriptor ldt[LDT_SIZE];
 47   u32 pid;
 48   const char *p_name;
 49 }Process;
 50
 51 typedef void (*TaskAddr)(void);
 52
 53 #define TASK_NAME_LEN 32
 54 typedef struct Task_
 55 {
 56   TaskAddr init_eip;
 57   u32 stack_size;
 58   char name[TASK_NAME_LEN];
 59 }Task;
 60
 61
 62 typedef struct Tss_
 63 {
 64   u32 backlink;
 65   u32 esp0;
 66   u32 ss0;
 67   u32 esp1;
 68   u32 ss1;
 69   u32 esp2;
 70   u32 ss2;
 71   u32 cr3;
 72   u32 eip;
 73   u32 flags;
 74   u32 eax;
 75   u32 ecx;
 76   u32 edx;
 77   u32 ebx;
 78   u32 esp;
 79   u32 ebp;
 80   u32 esi;
 81   u32 edi;
 82   u32 es;
 83   u32 cs;
 84   u32 ss;
 85   u32 ds;
 86   u32 fs;
 87   u32 gs;
 88   u32 ldt;
 89   u32 trap;
 90   u32 iobase;
 91 }Tss;
 92
 93 extern Process proc_table[];
 94 extern u8 task_stack[];
 95 extern Process *ready_process;
 96 extern Tss tss;
 97
 98 void init_proc(void);
 99
100 #endif


 42 typedef struct Process_
 43 {
 44   StackFrame regs;
 45   u16 ldt_sel;
 46   Descriptor ldt[LDT_SIZE];
 47   u32 pid;
 48   const char *p_name;
 49 }Process;
大概就是要維護 Process 這樣一個資料結構, 並設定好 LDT, 將 cs, eip 設定為執行某個 process 的位址 (使用 LDT 的 selector), 再用類似 jmp 或是 call 的方式去執行。但其實並不是使用 jmp, call, 而是使用 iret 這種很奇怪的方式, 先將 cs, eip push 到 stack, iret 會從 stack 取出 cs, eip, 從而執行這個 process, 最主要的目的是為了切換權限, 從 ring0 切到 ring1。而從 process ring1 會透過 timer isr 在切回 ring0。若沒有切換權限也許可以不用這麼奇怪的方式吧!

這章節花了我不少時間才弄懂, 不過就算我弄懂, 要完成一個切換 process 的程式碼, 從零的狀態靠自己寫出來應該是沒辦法, 不像有些演算法, 知道其方法, 就可以將程式碼寫出。光是 process 這部份就不容易, 再加上處理中斷的程式碼, 若是要用上 minix 上的技巧, 沒看過這種程式碼的話, 實在很難想到這樣的作法。

真開心我可以槓掉那句話, x86 process switch 系列是我從無到有的程式碼, 這系列的文章我做了很仔細的分析, 希望對有興趣的朋友有幫助。

中斷處理比我想像中還要麻煩, 很容易就出現某些差錯, 目前還不知道怎麼完整處理一個中斷, 隨便加上一些和書中不同的程式碼, 行為整個就出錯, 也沒有好的除錯方式, 很頭痛阿!

能正常執行這段 process 程式碼的時候, 真是讓我開心不已, 原來這就是 process。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。