熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Oracle >> 正文

Linux系統調用跟我學(1)

2013-11-13 15:38:39  來源: Oracle 

  作者雷鎮
  
  本文是Linux系統調用系列文章的第一篇對Linux系統調用的定義基本原理使用方法和注意事項大概作了一個介紹以便讀者對Linux系統調用建立一個大致的印象
  
  
  什麼是系統調用?
  
  
  Linux內核中設置了一組用於實現各種系統功能的子程序稱為系統調用用戶可以通過系統調用命令在自己的應用程序中調用它們從某種角度來看系統調用和普通的函數調用非常相似區別僅僅在於系統調用由操作系統核心提供運行於核心態而普通的函數調用由函數庫或用戶自己提供運行於用戶態二者在使用方式上也有相似之處在下面將會提到
  
  隨Linux核心還提供了一些C語言函數庫這些庫對系統調用進行了一些包裝和擴展因為這些庫函數與系統調用的關系非常緊密所以習慣上把這些函數也稱為系統調用
  
  
  Linux中共有多少個系統調用?
  
  
  這個問題可不太好回答就算讓Linus Torvaldz本人也不見得一下子就能說清楚
  
  在版內核中狹義上的系統調用共有你可以在<內核源碼目錄>/include/asmi/unistdh中找到它們的原本也可以通過命令man syscalls察看它們的目錄(man pages的版本一般比較老可能有很多最新的調用都沒有包含在內)廣義上的系統調用也就是以庫函數的形式實現的那些它們的個數從來沒有人統計過這是一件吃力不討好的活新內核不斷地在推出每一個新內核中函數數目的變化根本就沒有人在乎至少連內核的修改者本人都不在乎因為他們從來沒有發布過一個此類的聲明
  
  隨本文一起有一份經過整理的列表它不可能非常全面但常見的系統調用基本都已經包含在內那裡面只有不多的一部分是你平時用得到的本專欄將會有選擇的對它們進行介紹
  
  
  為什麼要用系統調用?
  
  
  實際上很多已經被我們習以為常的C語言標准函數在Linux平台上的實現都是靠系統調用完成的所以如果想對系統底層的原理作深入的了解掌握各種系統調用是初步的要求進一步若想成為一名Linux下編程高手也就是我們常說的Hacker其標志之一也是能對各種系統調用有透徹的了解
  
  即使除去上面的原因在平常的編程中你也會發現在很多情況下系統調用是實現你的想法的簡潔有效的途徑所以有可能的話應該盡量多掌握一些系統調用這會對你的程序設計過程帶來意想不到的幫助
  
  
  系統調用是怎麼工作的?
  
  
  一般的進程是不能訪問內核的它不能訪問內核所占內存空間也不能調用內核函數CPU硬件決定了這些(這就是為什麼它被稱作保護模式系統調用是這些規則的一個例外其原理是進程先用適當的值填充寄存器然後調用一個特殊的指令這個指令會跳到一個事先定義的內核中的一個位置(當然這個位置是用戶進程可讀但是不可寫的)在Intel CPU中這個由中斷x實現硬件知道一旦你跳到這個位置你就不是在限制模式下運行的用戶而是作為操作系統的內核所以你就可以為所欲為
  
  進程可以跳轉到的內核位置叫做sysem_call這個過程檢查系統調用號這個號碼告訴內核進程請求哪種服務然後它查看系統調用表(sys_call_table)找到所調用的內核函數入口地址接著就調用函數等返回後做一些系統檢查最後返回到進程(或到其他進程如果這個進程時間用盡)如果你希望讀這段代碼它在<內核源碼目錄>/kernel/entrySEntry(system_call)的下一行
  
  
  如何使用系統調用?
  
  
  先來看一個例子
  
  #include /*定義宏_syscall*/
  #include /*定義類型time_t*/
  _syscall(time_ttimetime_t *tloc) /*宏展開後得到time()函數的原型*/
  main()
  {
   time_t the_time;
   the_time=time((time_t *)); /*調用time系統調用*/
   printf(The time is %ld
  the_time);
  }
  
  
  
  系統調用time返回從格林尼治時間:開始到現在的秒數
  
  這是最標准的系統調用的形式宏_syscall()展開來得到一個函數原型稍後我會作詳細解釋但事實上如果把程序改成下面的樣子程序也可以運行得同樣的結果
  
  #include
  main()
  {
   time_t the_time;
   the_time=time((time_t *)); /*調用time系統調用*/
   printf(The time is %ld
  the_time);
  }
  
  
  
  這是因為在timeh中實際上已經用庫函數的形式實現了time這個系統調用替我們省掉了調用_syscall宏展開得到函數原型這一步
  
  大多數系統調用都在各種C語言函數庫中有所實現所以在一般情況下我們都可以像調用普通的庫函數那樣調用系統調用只在極個別的情況下我們才有機會用到_syscall*()這幾個宏
  
  
  _syscall*()是什麼?
  
  
  在unistdh裡定義了個宏分別是
  
  _syscall(typename)
  _syscall(typenametypearg)
  _syscall(typenametypeargtypearg)
  _syscall(typenametypeargtypeargtypearg)
  _syscall(typenametypeargtypeargtypeargtypearg)
  _syscall(typenametypeargtypeargtypeargtypeargtypearg)
  _syscall(typenametypeargtypeargtypeargtypeargtypeargtypearg)
  
  
  
  它們看起來似乎不太像宏但其實質和
  
  #define MAXSIZE
  
  
  
  裡面的MAXSIZE沒有任何區別
  
  它們的作用是形成相應的系統調用函數原型供我們在程序中調用我們很容易就能發現規律_syscall後面的數字和typeNargN的數目一樣多事實上_syscall後面跟的數字指明了展開後形成函數的參數的個數讓我們看一個實例就是剛剛用過的time系統調用
  
  _syscall(time_ttimetime_t *tloc)
  
  
  
  展開後的情形是這樣
  
  time_t time(time_t * tloc)
  {
   long __res;
   __asm__ volatile(int $x : =a (__res) : ()b ((long)(tloc)));
   do {
   if ((unsigned long)(__res) >= (unsigned long)()) {
   errno = (__res);
   __res = ;
   }
   return (time_t) (__res);
   } while () ;
  }
  
  
  
  可以看出_syscall(time_ttimetime_t *tloc)展開成一個名為time的函數原參數time_t就是函數的返回類型原參數time_t *和tloc分別構成新函數的參數事實上程序中用到的time函數的原型就是它
  
  
  errno是什麼?
  
  
  為防止和正常的返回值混淆系統調用並不直接返回錯誤碼而是將錯誤碼放入一個名為errno的全局變量中如果一個系統調用失敗你可以讀出errno的值來確定問題所在
  
  errno不同數值所代表的錯誤消息定義在errnoh中你也可以通過命令man errno來察看它們
  
  需要注意的是errno的值只在函數發生錯誤時設置如果函數不發生錯誤errno的值就無定義並不會被置為另外在處理errno前最好先把它的值存入另一個變量因為在錯誤處理過程中即使像printf()這樣的函數出錯時也會改變errno的值
  
  
  系統調用兼容性好嗎?
  
  
  很遺憾答案是不好但這決不意味著你的程序會三天兩頭的導致系統崩潰因為系統調用是Linux的內核提供的所以它們工作起來非常穩定對於此點無需絲毫懷疑在絕大多數的情況下系統調用要比你自己編寫的代碼可靠而高效的多
  
  但是在Linux的各版本內核之間系統調用的兼容性表現得並不像想象那麼好這是由Linux本身的性質決定的Linux是一群程序設計高手利用業余時間開發出來的他們中間的大部分人沒有把Linux當成一個嚴肅的商業軟件(現在的情況有些不同了隨著Linux商業公司和以Linux為生的人的增長不少人的腦筋發生了變化)結果就是如果新的方案在效率和兼容性上發生了矛盾他們往往捨棄兼容性而追求效率就這樣如果他們認為某個系統調用實現的比較糟糕他們就會毫不猶豫的作出修改有些時候甚至連接口也一起改掉了更可怕的是很多時候他們對自己的修改連個招呼也不打在任何文檔裡都找不到關於修改的提示這樣每當新內核推出的時候很可能都會悄悄的更新一些系統調用用戶編制的應用程序也會跟著出錯
  
  說到這裡你是不是感覺前途一片昏暗呢?呵呵不用太緊張如前面所說隨著越來越多的人把Linux當成自己的飯碗不兼容的情況也越來越罕見版本以後的Linux內核已經非常穩定了不過盡管如此你還是有必要在每個新內核推出之後對自己的應用程序進行兼容性測試以防止意外的發生
  
  
  該如何學習使用Linux系統調用呢?
  
  
  你可以用man 系統調用名稱的命令來查看各條系統調用的介紹但這首先要求你要有不錯的英語基礎其次還得有一定的程序設計和系統編程的功底man pages不會涉及太多的應用細節因為它只是一個手冊而非教程如果man pages所提
From:http://tw.wingwit.com/Article/program/Oracle/201311/17062.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.