Linux內核分析 —— 【實驗四:系統調用 】
“計算機科學領域的任何問題都可以通過增加一箇中間層來解決。”這句名言幾乎概括了整個計算機體系架構的設計要點。我們都知道計算機由硬件系統和軟件系統構成,後者是建立在前者的基礎上。硬件的性能在製造產商生產出來的時候就已經確定了,而軟件系統卻有着更大的可操作性。爲了更加便利,高效,有條不紊地利用硬件系統,我們增加了一層——操作系統。此外,操作系統之上還有應用軟件,操作系統除了要管理硬件資源,還要爲應用程序開發人員提供良好的環境來使應用軟件程序具有更好的兼容性,爲了達到這個目的,系統內核提供一系列具備預定功能的多內核函數,通過一組稱爲系統調用(system call)的接口呈現給用戶。系統調用把應用程序的請求傳給內核,調用相應的的內核函數完成所需的處理,將處理結果返回給應用程序。
一 系統調用概念
系統調用(System Call)是操作系統爲在用戶態運行的進程與硬件設備(如CPU、磁盤、打印機等)進行交互提供的一組接口。
二 系統調用的意義
(1)把用戶從底層的硬件編程中解放出來
(2)極大的提高了系統的安全性
(3)使用戶程序具有可移植性
三 系統調用機制
當用戶進程需要發生系統調用時,CPU 通過軟中斷切換到內核態開始執行內核系統調用函數。Linux 下有三種發生系統調用的方法:
(1)通過 glibc 提供的庫函數
(2)使用 syscall 函數直接調用
(3)通過 int 0x80指令陷入
總的來說,前兩種最終都會通過int 0x80指令陷入進入中斷處理程序。而系統調用也需要輸入輸出參數,例如實際的值,用戶態進程地址空間的變量的地址,甚至是包含指向用戶態函數的指針的數據結構的地址等。
system_call是linux中所有系統調用的入口點,每個系統調用至少有一個參數,即由eax傳遞的系統調用號,其他參數依次由ebx,ecx,edx,esi,edi,ebp傳入。
• 寄存器傳遞參數具有如下限制:
• 1)每個參數的長度不能超過寄存器的長度,即32位
• 2)在系統調用號(eax)之外,參數的個數不能超過6個(ebx,
ecx,edx,esi,edi,ebp)
注意:如果超過六個怎麼辦呢?可以傳入一個地址,地址所在地存放多個參數。
四 系統調用與應用編程接口
應用編程接口 (application program interface, API)只是一個函數定義,系統調用通過軟中斷向內核發出一個明確的請求,而gLibc庫定義的一些API引用了封裝例程(wrapper routine,唯一目的就是發佈系統調用),一般每個系統調用對應一個封裝例程,庫再用這些封裝例程定義出給用戶的API。如下圖
系統調用的三個層次依次是:xyz函數(API)、system_ call(中斷向量)和 sys_ xyz(中斷服務程序)。
五 例子
(1)通過API調用打開一個文件,並且寫入一串字符串:
syscall.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(){
int fd; //文件描述符
char *file="./test"; //文件路徑
char *buf="this is a string!"; //寫入字符串
fd=open(file,O_CREAT|O_RDWR); //打開文件,創建並讀寫的權限
write(fd,buf,strlen(buf)); //寫入字符串
close(fd); //關閉文件
return 0;
}
運行結果:
(2)通過int 0x80陷入
syscallasm.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
int main(){
int fd,len;
char *file="./test";
char *buf="this is a new string!";
len=strlen(buf);
asm volatile(
"movl %1,%%ebx\n\t" //將第一個參數file賦給ebx
"movl $102,%%ecx\n\t" //將立即數102賦值給ecx,代表讀寫文件
"movl $5,%%eax\n\t" //open函數的系統調用號是5,eax=5
"int $0x80\n\t"
"movl %%eax,%0\n\t" // fd=eax ,保存文件描述符
"movl %0,%%ebx\n\t" //ebx=fd
"movl %2,%%ecx\n\t" // ecx=buf
"movl %3,%%edx\n\t" // edx=len
"movl $4,%%eax\n\t" //eax=4,write函數的系統調用號爲4
"int $0x80\n\t"
"movl %0,%%ebx\n\t" //ebx=0
"movl $6,%%eax\n\t" //eax=6 ,close函數
"int $0x80\n\t"
:"=m"(fd)
:"m"(file),"m"(buf),"m"(len)
);
return 0;
}
運行結果:
=========== 王傑 原創作品轉載請註明出處==============
《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000 ”