想學linux c開發的博友或者正在學的博友,當你們的代碼裡出現系統調用,比如read、write、open等。你怎麼看、怎麼想?有沒有想一探究竟,深究一下系統調用的始終。準備好紙和筆,讓我們開始系統調用跟蹤之旅:
開始之前呢,先花費2分鐘概覽一下圖(1),在你的頭腦中做個快照,便于後面分析的了解,以read調用為例,展開分析。

read函數的聲明位于頭檔案#include
原型為:ssize_t read(int fd, void *buf, size_t
count)。
read函數在使用者空間的僞代碼:
說明:
第4行,寄存器eax儲存了read函數的系統調用号,在 檔案include/asm-i386/unistd.h裡有定義(#define __NR_read 3);第5~7行,将三個參數分别放入三個寄存器(通過寄存器來傳遞參數)
第8行,執行系統調用,進入核心;
第9行,擷取eax寄存器所儲存的函數返 回值。
第8行是一個中斷,執行完第8行後,已經進入了系統核心,中斷向量表中記錄0x80号的中斷處理程式開始執行,中斷向量表的初始化在arch/x86_64/kernel/traps.c中定義:
如紅框标注所示。IA32_SYSCALL_VECTOR是系統調用的中斷号,在include/asm-x86_64/hw_irq.h中定義:
正好是0x80。而system_call是系統調用的中斷處理函數指針,使用者執行int $0x80後會執行到這個函數,它在arch/x86_64/kernel/entry.S中定義:
SAVE_ALL是一個宏,在這include/asm-x86_64/calling.h檔案裡定義:
主要作用就是将各個寄存器壓入棧中。
cmpl $(nr_syscalls), %eax比較eax的值是否大于等于nr_syscalls,nr_syscalls是比最大有效系統調用号大1的值,在arch/i386/kernel/entry.S中定義:
syscall_table_size就是系統調用表的大小(機關:位元組),syscall_table_size其實是一個數組,數組裡存放的是各個系統調用函數的位址,元素類型是long型,除以4剛好是系統調用函數的個數。
如果從eax寄存器傳進來的系統調用号有效,那麼就執行第12行,在系統調用表裡找到相應的系統調用服務程式,sys_call_table在arch/i386/kernel/entry.S中定義:
*sys_call_table(,%eax,4)指的是sys_call_table裡偏移量為%eax*4上的那個值指向的函數,這裡%eax=3,那麼第5行的sys_read()函數就會被調用。sys_read()在/fs/read_write.c中定義:
asmlingage是一個宏,定義為:__attribute__((regparm(0))),作用是讓這個函數隻從棧上擷取參數(因為之前的SAVE_ALL将參數壓到了棧裡面)。
Fget_light:根據fd指定的索引,從目前程序描述符中取出相應的file對象,如果沒有找到指定的file對象,則傳回錯誤,如果找到了指定的file對象,則調用file_pos_read函數取出此次讀寫檔案的目前位置。
然後調用vfs_read執行檔案讀取操作,而這個函數最終調用file->f_op_read指向的函數[fs/read_write.c檔案中],代碼如下:
接下來調用file_pos_write()更新檔案的目前讀寫位置,調用fput_light更新檔案的引用計數。最後傳回讀取資料的位元組數。
<b>思考:</b>file->f_op_read指向的函數是哪個?鑒于篇幅,我們準備在下一篇博文詳解,繼而開始真正資料讀取之旅。<b></b>
當資料讀取完畢,需要傳回使用者态。以下即為推出核心,恢複各個寄存器的值,然後傳回使用者态的過程。
當執行完中斷處理程式後,後面會調用RESTORE_REGS來恢複各個寄存器:
RESTORE_REGS的定義:
RESTORE_INT_REGS的定義:
當你耐着性子看到這裡,真誠的說一聲謝謝。我的勞作是有意義的。我寫這篇博文的本意就是讓自己在頭腦形成一條清晰的脈絡,read系統調用的清晰脈絡。在平時分析問題的時候,沿着這條清晰的脈絡,将疑難一一排除。對了,寫這篇博文時,我參照的系統核心源碼版本是:
linux-2.6.0
Good luck for you!