操作系統運行環境與運行機制(系統調用篇)
系統調用:
用戶在編程是可以調用的操作系統功能(使CPU可以從用戶態陷入內核態)
應用程序,C函數,API,和內核函數關系
系統調用機制的設計
①中斷/異常機制
支持系統調用服務的實現:選擇一條陷入指令(訪管指令)即可
②選擇一條特殊指令:陷入指令(亦稱訪管指令)
引發異常完成用戶態到內核態的切換
③系統調用號和參數:
每個系統調用都實現給定一個編號(功能號)
④系統調用表:
存放系統調用服務例程入口地址
參數傳遞問題(怎樣實現用戶程序的參數(存在於用戶棧)傳遞給內核(存在於內核棧)?):
①由陷入指令自帶參數:陷入指令的長度有限,且還要攜帶系統調用功能號,只能自帶有限的參數
②通過通用寄存器傳遞參數:這些寄存器是操作系統和用戶程序都能訪問的,但寄存器的個數會限制傳遞參數的數量
③在內存中開辟專用堆棧區來傳遞參數
現代參數傳遞使用第2種方法
系統調用例子:
1 #include <unistd.h> 2 int main(){ 3 char string[5] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘!’, ‘\n’}; 4 write(1, string, 7); 5 return 0; 6 } 7 輸出結果:Hello!
這是用高級語言寫的程序,編譯後轉成匯編指令為:
1..section .data 2.output: 3. .ascii “Hello!\n” 4.output_end: 5. .equ len, output_end - output 6..section .text 7..globl _start 8._start: --- 9. movl $4, %eax #eax寄存器存放系統調用號| 10. movl $1, %ebx |這一段是write函數的簡要的匯編指令代碼 11. movl $output, %ecx | 12. movl $len, %edx | 13. int $0x80 #引發一次系統調用0x80 = 128(d)即為Linux中斷向量表中專門用於陷入指令的中斷號,調用異常機制 | 14. end: --- 15. movl $1, %eax #1這個系統調用的作用? ----- 1是系統調用號,對應的是返回退出程序 16. movl $0, %ebx | 這一段屬於return 0 簡要的匯編代碼 17. int $0x80 -----
系統調用執行過程
當CPU執行到特殊的陷入指令時:
1.中斷/異常機制:軟中斷發出中斷信號,硬件保護現場;通過查中斷向量表把控制權轉給系統調用總入口程序 (之前查到的都是包含中斷處理程序入口地址的中斷向量,現在是系統調用總入口程序地址)
2.系統調用總入口程序:保存現場;將參數保存在內核堆棧裏(參數傳遞);根據eax寄存器中的系統調用號,通過查系統調用表把控制權轉給相應的系統調用處理例程或內核函數
3.執行系統調用例程
4.恢復現場,返回用戶程序
Linux系統調用:
陷入指令中斷號選擇128號
int $0x80
門描述符
①系統初始化時:對IDT表中的128號門初始化
②門描述符(中斷描述符表)的2、3兩個字節設置為內核代碼段選擇符
0、1、6、7四個字節:偏移量(段選擇符從GDT中選擇段描述符,然後段描述符有段基地址,從而最終指向system_call(),由sched_init()中set_system_gate(0x80, &system_call)設置)
③門類型:15,陷阱門,沒有禁止中斷
④DPL:3,與用戶級別相同,因為只有當前執行程序的特權級大於等於門的DPL,特權級才能發生由用戶態變成內核態(數值越小特權級越高)
Linux內核中Include/ASM-1386/UNISTD.h文件:
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
存了大量的系統調用號
系統執行INT 0x80具體過程:
1.由於特權級的改變,要切換棧
用戶棧 → 內核棧
2.CPU從任務狀態段TSS中裝入新的棧指針(SS︰ESP),指向內核棧 PS.任務狀態段(Task State Segment, TSS)是x86架構電腦上是一個保存任務信息的數據結構,保存了內層堆棧指針
3.用戶棧的信息(SS︰ESP)、EFLAGS、用戶態CS 、EIP 寄存器的內容壓棧(返回用)
4.將EFLAGS壓棧後,復位TF,IF位保持不變 PS.TF(bit 8) [Trap flag] 將該位設置為1以允許單步調試模式,清零則禁用該模式。
5.用128在IDT中找到該門描述符,從中找出段選擇符裝入代碼段寄存器CS
6.段描述符中的基地址 + 陷阱門描述符中的偏移量 → 定位 system_call()的入口地址
Linux執行流程
對應的SAVE_ALL執行內容
底層工作總結:
1. 硬件壓棧:程序計數器等
2. 硬件從中斷向量裝入新的程序計數器等
3. 匯編語言過程保存寄存器值
4. 匯編語言過程設置新的堆棧
5. C語言中斷服務程序運行(例:讀並緩沖輸入)
6. 進程調度程序決定下一個將運行的進程
7. C語言過程返回至匯編代碼
8. 匯編語言過程開始運行新的當前進程
作者水平有限,文章肯定有錯還請各位指點!!!感謝!!!
參考鏈接:
http://blog.csdn.net/jn1158359135/article/details/7761011
操作系統運行環境與運行機制(系統調用篇)