該文件是系統(tǒng)調(diào)用實(shí)現(xiàn)的主要文件。為了弄清楚系統(tǒng)調(diào)用,首先應(yīng)該拿一個(gè)實(shí)例來(lái)看。
WinixJ系統(tǒng)僅僅實(shí)現(xiàn)了一個(gè)系統(tǒng)調(diào)用getpid(),但是其他系統(tǒng)調(diào)用的框架應(yīng)是完全相同的,只在函數(shù)實(shí)現(xiàn)細(xì)節(jié)上有所不同,因此我們打算以getpid()作為案例講解。
先看看getpid()呈現(xiàn)給用戶的調(diào)用接口吧:
POSIX規(guī)范的getpid()聲明如下:
pid_t getpid();等同于int getpid();為了簡(jiǎn)單起見(jiàn)我們就實(shí)現(xiàn)為int getpid();
它的代碼也十分簡(jiǎn)單:
1 int getpid()
2 {
3 int res;
4 __asm__ __volatile__ (
5 "movl $0x0, %%eax\n\t"
6 "movl $0x0, %%ebx\n\t"
7 "movl $0x0, %%ecx\n\t"
8 "movl $0x0, %%edx\n\t"
9 "int $0x30\n"
10 :"=a" (res)
11 :);
12 return res;
13 }
14
雖然有嵌入?yún)R編,但是也不難理解,看似是將eax、ebx、ecx、edx四個(gè)寄存器均傳值為0,然后調(diào)用軟中斷int 0x30(實(shí)際上應(yīng)該稱之為陷阱,陷阱和中斷還是有細(xì)微區(qū)別的,見(jiàn)
此博文),最后將返回的結(jié)果由eax賦值給res變量。很簡(jiǎn)單,不是嗎?
但是為何eax、ebx、ecx、edx都賦值了呢?調(diào)用int 0x30之后又發(fā)生什么了呢?
先說(shuō)調(diào)用int 0x30之后會(huì)發(fā)生的事情。我們看下面這段代碼,其實(shí)它在
此博文已經(jīng)出現(xiàn)過(guò),就是sys_call函數(shù)的實(shí)現(xiàn):
1 ; 系統(tǒng)調(diào)用框架,系統(tǒng)調(diào)用采用0x30號(hào)中斷向量,利用int 0x30指令產(chǎn)
2 ; 生一個(gè)軟中斷,之后便進(jìn)入sys_call函數(shù),該函數(shù)先調(diào)用save_all框
3 ; 架保存所有寄存器值,然后調(diào)用對(duì)應(yīng)系統(tǒng)調(diào)用號(hào)的入口函數(shù)完成系統(tǒng)調(diào)用
4 ; 注意!?。。?!系統(tǒng)調(diào)用默認(rèn)有三個(gè)參數(shù),分別利用ebx、ecx、edx來(lái)
5 ; 傳遞,其中eax保存系統(tǒng)調(diào)用號(hào)
6 sys_call:
7 save_all
8
9 sti
10
11 push ebx
12 push ecx
13 push edx
14 call [sys_call_table + eax * 4]
15 add esp, 4 * 3
16
17 recover_from_sys_call
18
19 cli
20
21 iretd
22
再看看save_all:
1 ; 一個(gè)宏,因?yàn)樗械膇rq中斷函數(shù)以及系統(tǒng)調(diào)用都是先保存現(xiàn)場(chǎng)并將數(shù)據(jù)段等堆棧段
2 ; 切換到內(nèi)核態(tài),因此,該操作所有的irq中斷入口函數(shù)均相同
3 ; 故寫成宏節(jié)省空間
4 %macro save_all 0
5 push eax
6 push ecx
7 push edx
8 push ebx
9 push ebp
10 push esi
11 push edi
12 push ds
13 push es
14 push fs
15 push gs
16 mov si, ss
17 mov ds, si
18 mov es, si
19 mov gs, si
20 mov fs, si
21 %endmacro
22
有了這一段代碼再說(shuō)int 0x30后都發(fā)生了什么就比較簡(jiǎn)單了,首先和發(fā)生時(shí)鐘中斷等硬件中斷類似,發(fā)生陷阱的時(shí)候CPU同樣先從當(dāng)前進(jìn)程對(duì)應(yīng)的TSS段中找到SS0和ESP0,然后將當(dāng)前進(jìn)程的SS/ESP/EFLAGS/CS/EIP五個(gè)寄存器值壓入SS0:ESP0,然后再去執(zhí)行save_all的代碼,save_all同樣是將所有的常規(guī)寄存器壓入堆棧,這樣一個(gè)過(guò)程之后就完成了從用戶態(tài)到內(nèi)核態(tài)的切換,之后再看sys_call代碼,壓入三個(gè)寄存器作為形參,然后調(diào)用sys_call_table[eax * 4]函數(shù)。到這兒肯定就明白了,原來(lái)getpid()函數(shù)是通過(guò)四個(gè)通用寄存器來(lái)向內(nèi)核態(tài)的系統(tǒng)調(diào)用函數(shù)傳遞參數(shù)的,這樣做是為了快捷。
還有一點(diǎn)就是install_sys_call_handler和install_sys_call兩個(gè)函數(shù)功能是不同的,說(shuō)白了,WinixJ就只有一個(gè)系統(tǒng)調(diào)用,sys_call,它是所有POSIX規(guī)定的系統(tǒng)調(diào)用的框架,所有系統(tǒng)調(diào)用的實(shí)現(xiàn)都在sys_call_table[]函數(shù)數(shù)組里。這樣,將getpid()作為第0號(hào)系統(tǒng)調(diào)用的意思就是將sys_call_table[0] = sys_call_getpid();然后getpid()函數(shù)在發(fā)生陷阱的時(shí)候會(huì)通過(guò)eax傳入系統(tǒng)調(diào)用號(hào)0,這樣在sys_call中就會(huì)調(diào)用sys_call_table[eax * 4],即sys_call_table[0],即sys_call_getpid(); 而install_sys_call_handler函數(shù)的作用是將IDT中0x30這一中斷描述符項(xiàng)設(shè)置成DPL = 3的陷阱門,該陷阱門的入口為sys_call,這樣,當(dāng)用戶程序調(diào)用int 0x30的時(shí)候就會(huì)調(diào)用sys_call;而install_sys_call的作用是將getpid()函數(shù)與第0號(hào)系統(tǒng)調(diào)用對(duì)應(yīng)起來(lái),即完成這一工作:sys_call_table[0] = sys_call_getpid();這樣,當(dāng)通過(guò)eax = 0傳入sys_call中時(shí),sys_call知道需要調(diào)用sys_call_table[]中的第幾個(gè)函數(shù)指針。
到此基本就明白了,還有最后一個(gè)細(xì)節(jié):為什么在系統(tǒng)調(diào)用完成后返回的時(shí)候不使用所有中斷返回時(shí)都使用的宏recover_all,而使用recover_from_sys_call?我們看看代碼:
1 ; 一個(gè)宏,恢復(fù)現(xiàn)場(chǎng)
2 %macro recover_all 0
3 pop gs
4 pop fs
5 pop es
6 pop ds
7 pop edi
8 pop esi
9 pop ebp
10 pop ebx
11 pop edx
12 pop ecx
13 pop eax
14 %endmacro
15
16 ; 注意從系統(tǒng)調(diào)用返回時(shí)不需要從棧中彈出eax的值,因?yàn)閑ax保存著調(diào)用
17 ; 對(duì)應(yīng)系統(tǒng)調(diào)用之后的返回值
18 %macro recover_from_sys_call 0
19 pop gs
20 pop fs
21 pop es
22 pop ds
23 pop edi
24 pop esi
25 pop ebp
26 pop ebx
27 pop edx
28 pop ecx
29 add esp, 4 * 1
30 %endmacro
仔細(xì)研究代碼應(yīng)該已經(jīng)明白了,其實(shí)getpid()是需要返回值的,恰好我們就使用eax作為返回值,這樣,在從堆棧中恢復(fù)現(xiàn)場(chǎng)的時(shí)候就不能將eax的值恢復(fù),因?yàn)閑ax還需要傳遞返回值。
至此,所有的系統(tǒng)調(diào)用細(xì)節(jié)應(yīng)該明白了,這里只是講述的一種系統(tǒng)調(diào)用的實(shí)現(xiàn),而有些操作系統(tǒng)的系統(tǒng)調(diào)用采用完全不同的風(fēng)格實(shí)現(xiàn)的,它沒(méi)有sys_call_table[]這樣的函數(shù)數(shù)組,而是將每個(gè)系統(tǒng)調(diào)用都設(shè)置成一個(gè)陷阱門,在IDT中都占有一個(gè)描述符項(xiàng),至于優(yōu)劣,其實(shí)沒(méi)有大的差別。WinixJ使用的方法正是Linux的方法。在此向Linus和Linux致敬。
posted on 2012-02-14 15:41
myjfm 閱讀(519)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
操作系統(tǒng)