歡迎您光臨本站 註冊首頁

Linux下增加系統調用的方法

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  1.linux系統調用的基本原理

  linux的系統調用形式與POSIX兼容,也是一套C語言函數名的集合.然而,linux系統調用的內部實現方式卻與DOC的INT 21H相似,它是經過INT 0X80H軟中斷進入后,再根據系統調用號分門別類地服務.

  從系統分析的角度,linux的系統調用涉及4個方面的問題.

  (1)與系統調用有關的數據結構和函數

  函數名以「sys_」開頭,後跟該系統調用的名字.例如,系統調用fork()的響應函數是sys_fork()(見Kernel/fork.c),exit()的響應函數是sys_exit()(見kernel/fork.c).

  文件include/asm/unisted.h為每個系統調用規定了唯一的編號.假設用name表示系統調用的名稱,那麼系統調用號與系統調用響應函數的關係是:以系統調用號_NR_name作為下標,可找出系統調用表sys_call_table(見

  arch/i386/kernel/entry.S)中對應表項的內容,它正好 是該系統調用的響應函數sys_name的入口地址.系統調用表sys_call_table記錄了各sys_name函數在表中的位 置,共190項.有了這張表,就很容易根據特定系統調用在表中的偏移量,找到對應的系統調用響應函數的入口地址.系統調用表共256項,餘下的項是可供用戶自己添加的系統調用空間.

  (2)進程的系統調用命令轉換為INT 0x80中斷的過程

  宏定義_syscallN()見include/asm/unisted.h)用於系統調用的格式轉換和參數的傳遞.N取0~5之間的整數.參數個數為N的系統調用由_syscallN()負責格式轉換和參數傳遞.系統調用號放入EAX寄存器,啟動INT 0x80 后,規定返回值送EAX寄存器.

  (3)系統調用功能模塊的初始化

  對系統調用的初始化也就是對INT 0x80的初始化.系統啟動時,彙編子程序setup_idt(見arch/i386/kernel/head.S)準備了1張256項的idt表,由 start_kernel()(見 init/main.c),trap_init()(見

  arch/i386/kernel/traps.c)調用的C語言宏定義

  set_system_gate(0x80,&system_call)(見include/asm/system.h)設置0x80號軟中斷的服務程序為 system_call(見

  arch/i386/kernel/entry.S),system.call就是所有系統調用的總入口.

  (4)內核如何為各種系統調用服務

  當進程需要進行系統調用時,必須以C語言函數的形式寫一句系統調用命令.該命令如果已在某個頭文件中由相應的_syscallN()展開,則用戶程序必須包含該文 件.當進程執行到用戶程序的系統調用命令時,實際上執行了由宏命令_syscallN()展開的函數.系統調用的參數 由各通用寄存器傳遞,然後執行INT 0x80,以內核態進入入口地址system_call.

  (5)ret_from_sys_call

  以ret_from_sys_call入口的彙編程序段在linux進程管理中起到了十分重要的作用.所有系統調用結束前以及大部分中斷服務返回前,都會跳轉至此處入口地址. 該段程序不僅僅為系統調用服務,它還處理中斷嵌套、CPU調度、信號等事務.

  2.通過修改內核源代碼添加系統調用

  通過以上分析linux系統調用的過程,將自己的系統調用加到內核中就是一件容易的事情.下面介紹一個實際的系統調用,並把它加到內核中去.要增加的系統調用是:inttestsyscall(),其功能是在控制終端屏幕上顯示hello world,執行成功后返回0.

  1編寫inttestsyscall()系統調用

  編寫一個系統調用意味著要給內核增加1個函數,將新函數放入文件kernel/sys.c中.新函數代碼如下:

  asmlingkage sys_testsyscall()

  { console_print("hello worldn");

  return 0;

  }

  2連接新的系統調用

  編寫了新的系統調用過程后,下一項任務是使內核的其餘部分知道這一程序的存在,然後重建包含新的系統調用的內核.為了把新的函數連接到已有的內核中去, 需要編輯2個文件:

  1).inculde/asm/unistd.h在這個文件中加入

  #define_NR_testsyscall 191

  2).are/i386/kernel/entry.s這個文件用來對指針數組初始化,在這個文件中增加一行:

  .long SYMBOL_NAME(_sys_tsetsycall)

  將.rept NR_syscalls-190改為NR_SYSCALLS-191,然後重新獎勵和運行新內核.

  3).使用新的系統調用

  在保證的C語言庫中沒有新的系統調用的程序段,必須自己建立其代碼如下

  #inculde<linux/unistd.h>

  _syscall0(int,testsyscall)

  main()

  {

  tsetsyscall();

  }

  在這裡使用了_syscall0()宏指令,宏指令本身在程序中將擴展成名為syscall()的函數,它在main()函數內部加以調用.在testsyscall()函數中, 預處理程序產生所有必要的機器指令代碼,包括用系統調用參數值載入相應的cpu寄存器, 然後執行int 0x80中斷指令.

  3.利用內核模塊添加系統調用

  模塊是內核的一部分,但是並沒有被編譯到內核裡面去.它們被分別編譯並連接成一組目標文件, 這些文件能被插入到正在運行的內核,或者從正在運行的內核中移走.內核模塊至少必須有2個函數:

  int_module和cleanup_module.第一個函數是在把模塊插入內核時調用的;第二個函數則在刪除該模塊時調用.由於內核模塊是內核的一部分,能訪問所有內核資源.根據對linux系統調用機制的分析,如果要增加系統調用,可以編寫自己的函數來實現,然後在sys_call_table表中增加一項,使該項中的指針指向自己編寫的函數,就可以實現系統調用.下面用該方法實現在控制終端上列印「hello world」 的系統調用testsyscall().

  1)編寫系統調用內核模塊

  #inculde(linux/kernel.h)

  #inculde(linux/module.h)

  #inculde(linux/modversions.h)

  #inculde(linux/sched.h)

  #inculde(asm/uaccess.h)

  #define_NR_testsyscall 191

  extern viod *sys_call table[];

  asmlinkage int testsyscall()

  { printf("hello worldn");

  return 0;

  }

  int init_module()

  { sys_call_table[_NR_tsetsyscall]=testsyscall;

  printf("system call testsyscall() loaded successn");

  return 0;

  }

  void cleanup_module()

  {

  }

  2)使用新的系統調用#define<linux/unistd.h>

  #define_NR_testsyscall 191

  _syscall0(int,testsyscall)

  main()

  {

  testsyscall();

  }

  3)編譯內核模塊並插入內核

  編譯內核的命令為:gcc -Wall -02 -DMODULE -D_KERNEL_-C syscall.c

  -Wall通知編譯程序顯示警告信息;參數-02 是關於代碼優化的設置, 內核模塊必須優化;參數-D_LERNEL通知頭文件向內核模塊提供正確的定義; 參數-D_KERNEL_通知頭文件,這個程序代碼將在內核模式下運行.編譯成功后將生成 syscall.0文件.使用insmod syscall.o命令將模塊插入內核后即可使用增加的系統調用.

  比較以上二種方法,筆者認為採用內核模塊的方法較好.這種方法可省去編譯新內核並用新內核重新 啟動的麻煩,這一優點對於代碼的調試是非常有價值的, 可以節省大量時間.


[火星人 ] Linux下增加系統調用的方法已經有580次圍觀

http://coctec.com/docs/linux/show-post-51251.html