系統呼叫執行過程分析
阿新 • • 發佈:2019-01-26
#####################################
作者:張卓
原創作品轉載請註明出處:《Linux作業系統分析》MOOC課程 http://www.xuetangx.com/courses/course-v1:ustcX+USTC001+_/about
#####################################
● 把使用者從底層的硬體程式設計中解放出來
● 極大的提高了系統的安全性
● 使用使用者程式具有可移植性
作業系統提供的API和系統呼叫的關係
應用程式設計介面(application program interace,API)和系統呼叫是不同的
● API只是一個函式定義
● 系統呼叫通過軟中斷向核心發生一個明確的請求
Libc庫定義的一些API引用了封裝例程(wrapper routine,唯一目的就是釋出系統呼叫)
● 一般每個系統呼叫對應一個封裝例程
● 庫再用這些例程定義出給使用者的API
不是每個API都對應一個特定的系統呼叫
● API可能直接提供使用者態的服務,如一些數學函式
● 一個單獨的API可能呼叫幾個系統呼叫
● 不同的API可能呼叫同一個系統呼叫
返回值
● 大部分封裝例程返回一個整數,其值的含義依賴於相應的系統呼叫
● -1在大多數的情況下表示核心不能滿足程序的請求
● Libc中定義的errno變數包含特定的出錯碼
在 Linux 平臺下有兩種方式來使用系統呼叫:利用封裝後的 C 庫(libc)或者通過彙編直接呼叫。其中通過組合語言來直接呼叫系統呼叫,是最高效地使用 Linux 核心服務的方法,因為最終生成的程式不需要與任何庫進行連結,而是直接和核心通訊。
下面我將藉助實驗樓的環境詳細分析使用庫函式API和C程式碼中嵌入彙編程式碼兩種方式使用同一個系統呼叫的過程。
通過簡單的編譯執行,可以得到如下的結果:
接下來,我們用GCC內嵌彙編的方式來執行系統呼叫:
通過下面的命令編譯,然後執行:
使用者程序通過呼叫系統提供的API,在API中執行類似與上面內嵌彙編的程式;進而執行system call 處理程式,在system call處理程式中執行真正的系統提供的服務程式,執行完畢返回退出。系統呼叫號將API和system call 緊密聯絡在一起。在這個過程中涉及到了,系統從使用者態切換到核心態執行程式的過程,從而可以說明系統呼叫也是一種特殊的中斷。中斷向量0x80對應system call。
本部落格參考:Linux 組合語言開發指南
系統呼叫列表參見http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/syscalls/syscall_32.tbl
作者:張卓
原創作品轉載請註明出處:《Linux作業系統分析》MOOC課程 http://www.xuetangx.com/courses/course-v1:ustcX+USTC001+_/about
#####################################
一 系統呼叫概述
作業系統為使用者態程序與硬體裝置進行互動提供了一組介面-系統呼叫● 把使用者從底層的硬體程式設計中解放出來
● 極大的提高了系統的安全性
● 使用使用者程式具有可移植性
作業系統提供的API和系統呼叫的關係
應用程式設計介面(application program interace,API)和系統呼叫是不同的
● API只是一個函式定義
● 系統呼叫通過軟中斷向核心發生一個明確的請求
Libc庫定義的一些API引用了封裝例程(wrapper routine,唯一目的就是釋出系統呼叫)
● 一般每個系統呼叫對應一個封裝例程
● 庫再用這些例程定義出給使用者的API
不是每個API都對應一個特定的系統呼叫
● API可能直接提供使用者態的服務,如一些數學函式
● 一個單獨的API可能呼叫幾個系統呼叫
● 不同的API可能呼叫同一個系統呼叫
返回值
● 大部分封裝例程返回一個整數,其值的含義依賴於相應的系統呼叫
● -1在大多數的情況下表示核心不能滿足程序的請求
● Libc中定義的errno變數包含特定的出錯碼
二 系統呼叫的執行過程分析
下面我將藉助實驗樓的環境詳細分析使用庫函式API和C程式碼中嵌入彙編程式碼兩種方式使用同一個系統呼叫的過程。
這簡單的程式用庫函式的方式使用了兩個系統呼叫:5 和 31 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <fcntl.h> 5 6 int main(int argc, const char *argv[]) 7 { 8 int fd = 0; 9 int ret = 0; 10 char buf[100] = {0}; 11 char *filename = "1569.txt"; 12 13 fd = open(filename, O_RDONLY); 14 if( 0 > fd){ 15 perror("Failed to open"); 16 exit(1); 17 } 18 19 ret = read(fd, buf, 50); 20 if(ret < 0){ 21 perror("Failed to read"); 22 close(fd); 23 exit(1); 24 } 25 printf("%s\n",buf); 26 27 return 0; 28 }
通過簡單的編譯執行,可以得到如下的結果:
接下來,我們用GCC內嵌彙編的方式來執行系統呼叫:
和 DOS 一樣,Linux 下的系統呼叫也是通過中斷(int 0x80)來實現的。在執行 int 80 指令時,暫存器 eax 中存放的是系統呼叫的功能號,而傳給系統呼叫的引數則必須按順序放到暫存器 ebx,ecx,edx,esi,edi 中,當系統呼叫完成之後,返回值可以在暫存器 eax 中獲得。所有的系統呼叫功能號都可以在檔案 /usr/include/bits/syscall.h 中找到。1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <fcntl.h> 5 6 int main(int argc, const char *argv[]) 7 { 8 int fd = 0; 9 int ret = 0; 10 char buf[100] = {0}; 11 char *filename = "1569.txt"; 12 13 //fd = open("1569.txt", O_RDONLY); 14 asm volatile( 15 "movl %1,%%ebx\n\t" /* 用ebx儲存第一個引數 */ 16 "movl %2,%%ecx\n\t" /* 用ecx儲存第二個引數 */ 17 "movl $0x5,%%eax\n\t" /* 用eax傳遞系統呼叫號,5表示sys_open */ 18 "int $0x80\n\t" /* 系統通過 int $0x80這條指令產生一個軟中斷,執行系統呼叫 */ 19 "movl %%eax, %0\n\t" /* 和其它函式一樣,返回值也是儲存在eax暫存器之中 */ 20 :"=m"(fd) 21 :"b"(filename),"c"(O_RDONLY) 22 ); 23 if( 0 > fd){ 24 perror("Failed to open"); 25 exit(1); 26 } 27 28 //ret = read(fd, buf, 50); 29 asm volatile( 30 "movl %1,%%ebx;" 31 "movl %2,%%ecx;" 32 "movl %3,%%edx;" 33 "movl $0x3,%%eax;" 34 "int $0x80;" 35 "movl %%eax, %0;" 36 :"=m"(ret) 37 :"b"(fd),"c"(buf),"d"(50) 38 ); 39 if(ret < 0){ 40 perror("Failed to read"); 41 close(fd); 42 exit(1); 43 } 44 printf("%s\n",buf); 45 46 return 0; 47 } 48
通過下面的命令編譯,然後執行:
gcc read_asm.c -o read_asm -m32
可以看到執行結果和上面的一樣:三 總結
從上面實驗的內容我們得出,系統呼叫的執行過程,可以用下面的一個圖表示出來:使用者程序通過呼叫系統提供的API,在API中執行類似與上面內嵌彙編的程式;進而執行system call 處理程式,在system call處理程式中執行真正的系統提供的服務程式,執行完畢返回退出。系統呼叫號將API和system call 緊密聯絡在一起。在這個過程中涉及到了,系統從使用者態切換到核心態執行程式的過程,從而可以說明系統呼叫也是一種特殊的中斷。中斷向量0x80對應system call。
本部落格參考:Linux 組合語言開發指南
系統呼叫列表參見http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/syscalls/syscall_32.tbl