日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux下简单的系统调用

發(fā)布時(shí)間:2025/3/15 linux 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下简单的系统调用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
原創(chuàng) 2017年03月17日 21:31:20

楊金龍 + 原創(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。