Linux下简单的系统调用
楊金龍 + 原創(chuàng)作品轉(zhuǎn)載請(qǐng)注明出處 + 《Linux內(nèi)核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000
??本周是Linux系統(tǒng)分析課程的第四周課程,本周主要講Linux系統(tǒng)調(diào)用的過程,具體知識(shí)點(diǎn)和實(shí)驗(yàn)結(jié)果總結(jié)如下。
系統(tǒng)調(diào)用的相關(guān)知識(shí)
系統(tǒng)調(diào)用:系統(tǒng)調(diào)用只是一個(gè)特殊的中斷。我們通過庫函數(shù)和系統(tǒng)調(diào)用打交道,庫函數(shù)把系統(tǒng)調(diào)用封裝起來。
1、儲(chǔ)備知識(shí)——內(nèi)核態(tài)和用戶態(tài)
內(nèi)核態(tài):在高執(zhí)行級(jí)別下,代碼可以執(zhí)行特權(quán)指令,訪問任意的物理內(nèi)存,這種CPU執(zhí)行級(jí)別就對(duì)應(yīng)著內(nèi)核態(tài)。
用戶態(tài):在用戶態(tài)級(jí)別下,代碼的掌控范圍會(huì)受到限制,只能在對(duì)應(yīng)級(jí)別允許的范圍內(nèi)活動(dòng)。
注:Intel x86CPU有四種不同的執(zhí)行級(jí)別0-3,Linux只使用了其中的0級(jí)和3級(jí)分別來表示內(nèi)核態(tài)和用戶態(tài)。
2、為什么有權(quán)限級(jí)別的劃分?
若沒有用戶態(tài)和內(nèi)核態(tài)的劃分,用戶寫的不健壯的程序就可以執(zhí)行特權(quán)指令時(shí),就很容易是系統(tǒng)崩潰。操作系統(tǒng)發(fā)展過程中劃分了用戶態(tài)和內(nèi)核態(tài),是系統(tǒng)更穩(wěn)定的機(jī)制。
3、寄存器在系統(tǒng)調(diào)用中的作用
Cs寄存器的最低兩位表明了當(dāng)前代碼的特權(quán)級(jí)。CPU每條指令的讀取都是通過CS:EIP這兩個(gè)寄存器:其中CS是代碼段選擇寄存器,EIP是偏移量寄存器。
4、內(nèi)存地址空間
一般在Linux中,地址空間是一個(gè)顯著的標(biāo)志:0xc0000000以上的地址空間只能在內(nèi)核態(tài)下訪問,0x0000000–0xbfffffff的地址空間在兩種狀態(tài)下都可以訪問。
注:產(chǎn)生中斷是從用戶態(tài)進(jìn)入內(nèi)核態(tài)的主要方式。
5、寄存器上下文:
5.1從用戶態(tài)切換到內(nèi)核態(tài)時(shí):
必須保存用戶態(tài)的寄存器上下文;
同時(shí)把內(nèi)核態(tài)的寄存器的值放到寄存器中。
5.2中斷/int指令會(huì)在堆棧上保存一些寄存器的值:
如用戶態(tài)棧頂?shù)刂?#xff1b;
當(dāng)前的狀態(tài)字;
當(dāng)時(shí)的CS:EIP的值。
5.3中斷發(fā)生后的第一件事就是保存現(xiàn)場(chǎng):
保存現(xiàn)場(chǎng):就是進(jìn)入中斷程序,保存需要用到的寄存器的數(shù)據(jù)。
5.4中斷處理結(jié)束前最后一件事就是恢復(fù)現(xiàn)場(chǎng):
恢復(fù)現(xiàn)場(chǎng):就是退出中斷程序恢復(fù)保存寄存器的數(shù)據(jù)。
注:Iret指令和中斷信號(hào)(包括int指令)發(fā)生時(shí)的CPU做的動(dòng)作整好相反。
中斷處理的完整過程
如下圖所示
中斷指令interrupt(ex:int 0x80)開始進(jìn)行系統(tǒng)調(diào)用;
保存當(dāng)前CS:EIP,SS:ESP,eflags的值到內(nèi)核堆棧,同時(shí)加載了中斷服務(wù)程序的地址到CS:EIP以及內(nèi)核堆棧棧頂指針到SS:ESP中。Int指令完成上述操作過程。
內(nèi)核代碼,完成中斷服務(wù):
發(fā)生進(jìn)程調(diào)度,則保存調(diào)度時(shí)的現(xiàn)場(chǎng),進(jìn)行調(diào)度,完成調(diào)度后再恢復(fù)現(xiàn)場(chǎng);
不發(fā)生進(jìn)程調(diào)度,則恢復(fù)之前的保存現(xiàn)場(chǎng):iret - pop cs:EIP/SS:ESP/eflags from kernel stack.
系統(tǒng)調(diào)用的意義
1、操作系統(tǒng)為用戶態(tài)進(jìn)程與硬件設(shè)備進(jìn)行交互提供了一組接口——系統(tǒng)調(diào)用。
把用戶從底層的硬件編程中解放出來;
極大的提高了系統(tǒng)的安全性;
使用戶程序具有可移植性。
2、操作系統(tǒng)提供的API和系統(tǒng)調(diào)用的關(guān)系。
應(yīng)用編程接口(Application program interface,API)和系統(tǒng)調(diào)用時(shí)不同的;
API只是一個(gè)函數(shù)定義;
系統(tǒng)調(diào)用通過軟中斷向內(nèi)核發(fā)出一個(gè)明確的請(qǐng)求。
Libc庫定義的一些API引用了封裝例程(wrapper routine,唯一的目的就是發(fā)布系統(tǒng)調(diào)用):一般每個(gè)系統(tǒng)調(diào)用對(duì)應(yīng)一個(gè)封裝例程。庫再用這些封裝例程定義給出用戶的API。
3、不是每個(gè)API都對(duì)應(yīng)一個(gè)特定的系統(tǒng)調(diào)用:
API可能直接提供用戶態(tài)的服務(wù);
一個(gè)單獨(dú)的API可能調(diào)用幾個(gè)系統(tǒng)調(diào)用;
不同的API可能調(diào)用了同一個(gè)系統(tǒng)調(diào)用。
4、返回值
大部分封裝例程返回一個(gè)整數(shù),其值的含義依賴于相應(yīng)的系統(tǒng)調(diào)用;
-1在多數(shù)情況下表示內(nèi)核不能滿足進(jìn)程的請(qǐng)求;
Libc中定義的errno變量包含特定的出錯(cuò)碼。
注:系統(tǒng)調(diào)用的三層皮:API,中斷向量,中斷服務(wù)程序
5、系統(tǒng)調(diào)用的服務(wù)例程:
5.1 當(dāng)用戶態(tài)進(jìn)程調(diào)用一個(gè)系統(tǒng)調(diào)用時(shí),CPU切換內(nèi)核態(tài)并開始執(zhí)行一個(gè)內(nèi)核函數(shù)。
在Linux中是通過執(zhí)行int $0x80來執(zhí)行系統(tǒng)調(diào)用的,這條匯編指令產(chǎn)生向量為128的編程異常;
Inter Pentium II中引入了sysenter指令(快速系統(tǒng)調(diào)用),2,6已經(jīng)支持。
5.2 傳參:
內(nèi)核實(shí)現(xiàn)了很多不同的系統(tǒng)調(diào)用;
進(jìn)程必須指明需要哪個(gè)系統(tǒng)調(diào)用,這需要使用EAX寄存器傳遞一個(gè)名為系統(tǒng)調(diào)用號(hào)的參數(shù)
5.3 系統(tǒng)調(diào)用也需要輸入輸出參數(shù),例如:
實(shí)際的值;
用戶態(tài)進(jìn)程地址空間的變量的地址;
甚至包含指向用戶態(tài)函數(shù)的指針的數(shù)據(jù)結(jié)構(gòu)的地址。
System call是Linux中所有系統(tǒng)調(diào)用的入口點(diǎn),每個(gè)系統(tǒng)調(diào)用至少有一個(gè)參數(shù),即由eax
5.3 傳遞的系統(tǒng)調(diào)用號(hào);
系統(tǒng)調(diào)用號(hào)將xyz和sys_xyz關(guān)聯(lián)起來;用eax寄存器來傳遞參數(shù);
一個(gè)應(yīng)用程序調(diào)用fork()封裝例程,那么在執(zhí)行int $0x80之前就把EAX寄存器的值置為2(即 NR fork);
這個(gè)寄存器的設(shè)置是libc庫中的封裝例程進(jìn)行的,因此用戶一般不關(guān)心系統(tǒng)調(diào)用號(hào);
進(jìn)入sys,call之后,立即將EAX的值壓入內(nèi)核堆棧;
5.4 寄存器參數(shù)具有如下限制:
每個(gè)參數(shù)的長(zhǎng)度不能超過寄存器的長(zhǎng)度,即32位;
在系統(tǒng)調(diào)用號(hào)(EAX)之外,參數(shù)的個(gè)數(shù)不能超過6個(gè)(ebx,ecx,edx,esi,edi,ebp)。
實(shí)驗(yàn)代碼
實(shí)驗(yàn)過程中調(diào)用mkdir系統(tǒng)函數(shù),mkdir調(diào)用號(hào)為39,函數(shù)原型如下:
int mkdir(const char *path, mode_t mode);
參數(shù):
path是目錄名
mode是目錄權(quán)限
返回值:
返回0 表示成功, 返回 -1表示錯(cuò)誤,并且會(huì)設(shè)置errno值。
通過C代碼調(diào)用
mkdir.c
#include <stdio.h> #include <sys/stat.h> #include <sys/types.h>int main() {int ret = 0;ret = mkdir("./test", 0777);if(ret == 0){printf("Mkdir success!\n") ; }else printf("Mkdir failed!\n");return 0; }通過嵌入式匯編調(diào)用
mkdir_asm.c
#include <stdio.h>int main() {int ret = 0;char *dir = "./test_asm";int mode = 0777;asm volatile("movl $39, %%eax\n\t""int $0x80\n\t""movl %%eax, %0\n\t": "=m"(ret): "b"(dir), "c"(mode) );if(ret == 0)printf("Mkdir through asm success!\n");else printf("Mkdir through asm failed!\n");return 0; }實(shí)驗(yàn)總結(jié)
通過對(duì)系統(tǒng)調(diào)用的兩種代碼實(shí)現(xiàn)方法的分析,我們可以知道C語言的API只不過是對(duì)Linux底層系統(tǒng)調(diào)用的一次封裝而已,本質(zhì)上是通過系統(tǒng)中斷實(shí)現(xiàn)的。
總結(jié)
以上是生活随笔為你收集整理的Linux下简单的系统调用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 默认sql mode_MyS
- 下一篇: linux 找不到swap分区,Linu