?? 6.html
字號:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312"> <META NAME="GENERATOR" CONTENT="《良友》v2.1, 作者:安富國,http://winking.126.com"> <TITLE>系統調用</TITLE></HEAD><BODY style="font-family: 宋體; font-size: 9pt"> <CENTER><TABLE CELLSPACING=10 CELLPADDING=10 WIDTH="60%" BGCOLOR="#FFB693" ><TR><TD ALIGN=CENTER><FONT SIZE=+2><!--標題由此開始-->系統調用</TD></TR></TABLE></CENTER><p><h3>目 錄</h3><!--目錄由此開始--><A NAME="Content" ID="Content"></A><OL><LI><A HREF="#I443">系統調用</A></LI><OL><LI><A HREF="#I444">系統調用簡述</A></LI><OL><LI><A HREF="#I445">宏</A></LI><LI><A HREF="#I446">系統調用表</A></LI><LI><A HREF="#I447">系統調用入口函數</A></LI></OL><LI><A HREF="#I448">系統調用實現過程</A></LI><OL><LI><A HREF="#I449">函數名約定</A></LI><LI><A HREF="#I450">系統調用號</A></LI><LI><A HREF="#I451">系統調用表</A></LI><LI><A HREF="#I452">從ptrace系統調用命令到INT 0X80中斷請求的轉換</A></LI><LI><A HREF="#I453">系統調用功能模塊的初始化</A></LI><LI><A HREF="#I454">內核服務</A></LI></OL><LI><A HREF="#I455">代碼分析:mlock()</A></LI><OL><LI><A HREF="#I456">主要數據結構</A></LI><LI><A HREF="#I457">重要常量</A></LI><LI><A HREF="#I458">代碼函數功能分析</A></LI></OL><LI><A HREF="#I459">添加新調用</A></LI><OL><LI><A HREF="#I460">例子一</A></LI><LI><A HREF="#I461">例子二</A></LI></OL></OL></OL><hr><br><A NAME="I443" ID="I443"></A><center><b><font size=+2>系統調用</font></b></center><br> 在系統中真正被所有進程都使用的內核通信方式是系統調用。例如當進程請求內核服務時,就使用的是系統調用。一般情況下,進程是不能夠存取系統內核的。它不能存取內核使用的內存段,也不能調用內核函數,CPU的硬件結構保證了這一點。只有系統調用是一個例外。進程使用寄存器中適當的值跳轉到內核中事先定義好的代碼中執行,(當然,這些代碼是只讀的)。在Intel結構的計算機中,這是由中斷0x80實現的。<p> 進程可以跳轉到的內核中的位置叫做system_call。在此位置的過程檢查系統調用號,它將告訴內核進程請求的服務是什么。然后,它再查找系統調用表sys_call_table,找到希望調用的內核函數的地址,并調用此函數,最后返回。<p> 所以,如果希望改變一個系統調用的函數,需要做的是編寫一個自己的函數,然后改變sys_call_table中的指針指向該函數,最后再使用cleanup_module將系統調用表恢復到原來的狀態<p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I444" ID="I444"></A><center><b><font size=+2>系統調用簡述</font></b></center><br> linux里面的每個系統調用是靠一些宏,,一張系統調用表,一個系統調用入口來完成的。<br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I445" ID="I445"></A><center><b><font size=+2>宏</font></b></center><br> 宏就是_syscallN(type,name,x...),N是系統調用所需的參數數目,type是返回類型,name即面向用戶的系統調用函數名,x...是調用參數,個數即為N。<br> 例如:<br>#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \<br>type name(type1 arg1,type2 arg2,type3 arg3) \<br>{ \<br>long __res; \<br>__asm__ volatile ("int $0x80" \<br> : "=a" (__res) \<br> : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \<br> "d" ((long)(arg3))); \<br>if (__res>=0) \<br> return (type) __res; \<br>errno=-__res; \<br>return -1; \<br>}<br>(這是2.0.33版本)<br> 這些宏定義于include\asm\Unistd.h,這就是為什么你在程序中要包含這個頭文件的原因。該文件中還以__NR_name的形式定義了164個常數,這些常數就是系統調用函數name的函數指針在系統調用表中的偏移量。<p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I446" ID="I446"></A><center><b><font size=+2>系統調用表</font></b></center><br> 系統調用表定義于entry.s的最后。<br> 這個表按系統調用號(即前面提到的__NR_name)排列了所有系統調用函數的指針,以供系統調用入口函數查找。從這張表看得出,linux給它所支持的系統調用函數取名叫sys_name。<p><br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I447" ID="I447"></A><center><b><font size=+2>系統調用入口函數</font></b></center><br> 系統調用入口函數定義于entry.s:<p>ENTRY(system_call)<br> pushl %eax # save orig_eax<br> SAVE_ALL<br>#ifdef __SMP__<br> ENTER_KERNEL<br>#endif<br> movl $-ENOSYS,EAX(%esp)<br> cmpl $(NR_syscalls),%eax<br> jae ret_from_sys_call<br> movl SYMBOL_NAME(sys_call_table)(,%eax,4),%eax<br> testl %eax,%eax<br> je ret_from_sys_call<br>#ifdef __SMP__<br> GET_PROCESSOR_OFFSET(%edx)<br> movl SYMBOL_NAME(current_set)(,%edx),%ebx<br>#else<br> movl SYMBOL_NAME(current_set),%ebx<br>#endif<br> andl $~CF_MASK,EFLAGS(%esp)<br> movl %db6,%edx<br> movl %edx,dbgreg6(%ebx)<br> testb $0x20,flags(%ebx)<br> jne 1f<br> call *%eax<br> movl %eax,EAX(%esp)<br> jmp ret_from_sys_call<br> 這段代碼現保存所有的寄存器值,然后檢查調用號(__NR_name)是否合法(在系統調用表中查找),找到正確的函數指針后,就調用該函數(即你真正希望內核幫你運行的函數)。運行返回后,將調用ret_from_sys_call,這里就是著名的進程調度時機之一。<br> 當在程序代碼中用到系統調用時,編譯器會將上面提到的宏展開,展開后的代碼實際上是將系統調用號放入ax后移用int 0x80使處理器轉向系統調用入口,然后查找系統調用表,進而由內核調用真正的功能函數。<br> 自己添加過系統調用的人可能知道,要在程序中使用自己的系統調用,必須顯示地應用宏_syscallN。<br> 而對于linux預定義的系統調用,編譯器在預處理時自動加入宏_syscall3(int,ioctl,arg1,arg2,arg3)并將其展開。所以,并不是ioctl本身是宏替換符,而是編譯器自動用宏聲明了ioctl這個函數。<p><br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I448" ID="I448"></A><center><b><font size=+2>系統調用實現過程</font></b></center><br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I449" ID="I449"></A><center><b><font size=+2>函數名約定</font></b></center><br>系統調用響應函數的函數名約定<br> 函數名以“sys_”開頭,后跟該系統調用的名字,由此構成164個形似sys_name()的函數名。因此,系統調用ptrace()的響應函數是sys_ptrace() (kernel/ptrace.c)。<p><br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I450" ID="I450"></A><center><b><font size=+2>系統調用號</font></b></center><br>系統調用號<br> 文件include/asm/unistd.h為每個系統調用規定了唯一的編號:<p>#define __NR_setup 0<br>#define __NR_exit 1<br>#define __NR_fork 2<br>… …<br>#define __NR_ptrace 26<p> 以系統調用號__NR_name作為下標,找出系統調用表sys_call_table (arch/i386/kernel/entry.S)中對應表項的內容,正好就是該系統調用的響應函數sys_name的入口地址。<p><p>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -