乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      系統(tǒng)調(diào)用跟我學(xué)(1)

       ydzhang 2009-11-03

      什么是系統(tǒng)調(diào)用?

      Linux內(nèi)核中設(shè)置了一組用于實現(xiàn)各種系統(tǒng)功能的子程序,稱為系統(tǒng)調(diào)用。用戶可以通過系統(tǒng)調(diào)用命令在自己的應(yīng)用程序中調(diào)用它們。從某種角度來看,系統(tǒng)調(diào)用和普通的函數(shù)調(diào)用非常相似。區(qū)別僅僅在于,系統(tǒng)調(diào)用由操作系統(tǒng)核心提供,運行于核心態(tài);而普通的函數(shù)調(diào)用由函數(shù)庫或用戶自己提供,運行于用戶態(tài)。二者在使用方式上也有相似之處,在下面將會提到。

      隨Linux核心還提供了一些C語言函數(shù)庫,這些庫對系統(tǒng)調(diào)用進行了一些包裝和擴展,因為這些庫函數(shù)與系統(tǒng)調(diào)用的關(guān)系非常緊密,所以習(xí)慣上把這些函數(shù)也稱為系統(tǒng)調(diào)用。





      回頁首


      Linux中共有多少個系統(tǒng)調(diào)用?

      這個問題可不太好回答,就算讓Linus Torvaldz本人也不見得一下子就能說清楚。

      在2.4.4版內(nèi)核中,狹義上的系統(tǒng)調(diào)用共有221個,你可以在<內(nèi)核源碼目錄>/include/asm-i386/unistd.h中找到它們的原本,也可以通過命令"man 2 syscalls"察看它們的目錄(man pages的版本一般比較老,可能有很多最新的調(diào)用都沒有包含在內(nèi))。廣義上的系統(tǒng)調(diào)用,也就是以庫函數(shù)的形式實現(xiàn)的那些,它們的個數(shù)從來沒有人統(tǒng)計過,這是一件吃力不討好的活,新內(nèi)核不斷地在推出,每一個新內(nèi)核中函數(shù)數(shù)目的變化根本就沒有人在乎,至少連內(nèi)核的修改者本人都不在乎,因為他們從來沒有發(fā)布過一個此類的聲明。

      隨本文一起有一份經(jīng)過整理的列表,它不可能非常全面,但常見的系統(tǒng)調(diào)用基本都已經(jīng)包含在內(nèi),那里面只有不多的一部分是你平時用得到的,本專欄將會有選擇的對它們進行介紹。





      回頁首


      為什么要用系統(tǒng)調(diào)用?

      實際上,很多已經(jīng)被我們習(xí)以為常的C語言標準函數(shù),在Linux平臺上的實現(xiàn)都是靠系統(tǒng)調(diào)用完成的,所以如果想對系統(tǒng)底層的原理作深入的了解,掌握各種系統(tǒng)調(diào)用是初步的要求。進一步,若想成為一名Linux下編程高手,也就是我們常說的Hacker,其標志之一也是能對各種系統(tǒng)調(diào)用有透徹的了解。

      即使除去上面的原因,在平常的編程中你也會發(fā)現(xiàn),在很多情況下,系統(tǒng)調(diào)用是實現(xiàn)你的想法的簡潔有效的途徑,所以有可能的話應(yīng)該盡量多掌握一些系統(tǒng)調(diào)用,這會對你的程序設(shè)計過程帶來意想不到的幫助。





      回頁首


      系統(tǒng)調(diào)用是怎么工作的?

      一般的,進程是不能訪問內(nèi)核的。它不能訪問內(nèi)核所占內(nèi)存空間也不能調(diào)用內(nèi)核函數(shù)。CPU硬件決定了這些(這就是為什么它被稱作"保護模式")。系統(tǒng)調(diào)用是這些規(guī)則的一個例外。其原理是進程先用適當(dāng)?shù)闹堤畛浼拇嫫?,然后調(diào)用一個特殊的指令,這個指令會跳到一個事先定義的內(nèi)核中的一個位置(當(dāng)然,這個位置是用戶進程可讀但是不可寫的)。在Intel CPU中,這個由中斷0x80實現(xiàn)。硬件知道一旦你跳到這個位置,你就不是在限制模式下運行的用戶,而是作為操作系統(tǒng)的內(nèi)核--所以你就可以為所欲為。

      進程可以跳轉(zhuǎn)到的內(nèi)核位置叫做sysem_call。這個過程檢查系統(tǒng)調(diào)用號,這個號碼告訴內(nèi)核進程請求哪種服務(wù)。然后,它查看系統(tǒng)調(diào)用表(sys_call_table)找到所調(diào)用的內(nèi)核函數(shù)入口地址。接著,就調(diào)用函數(shù),等返回后,做一些系統(tǒng)檢查,最后返回到進程(或到其他進程,如果這個進程時間用盡)。如果你希望讀這段代碼,它在<內(nèi)核源碼目錄>/kernel/entry.S,Entry(system_call)的下一行。





      回頁首


      如何使用系統(tǒng)調(diào)用?

      先來看一個例子:

      #include<linux/unistd.h> /*定義宏_syscall1*/
                  #include<time.h>     /*定義類型time_t*/
                  _syscall1(time_t,time,time_t *,tloc)    /*宏,展開后得到time()函數(shù)的原型*/
                  main()
                  {
                  time_t the_time;
                  the_time=time((time_t *)0); /*調(diào)用time系統(tǒng)調(diào)用*/
                  printf("The time is %ld\n",the_time);
                  }
                  系統(tǒng)調(diào)用time返回從格林尼治時間1970年1月1日0:00開始到現(xiàn)在的秒數(shù)。
                  這是最標準的系統(tǒng)調(diào)用的形式,宏_syscall1()展開來得到一個函數(shù)原型,稍后我會作詳細解釋。但事實上,如果把程序改成下面的樣子,程序也可以運行得同樣的結(jié)果。
                  #include<time.h>
                  main()
                  {
                  time_t the_time;
                  the_time=time((time_t *)0); /*調(diào)用time系統(tǒng)調(diào)用*/
                  printf("The time is %ld\n",the_time);
                  }

      這是因為在time.h中實際上已經(jīng)用庫函數(shù)的形式實現(xiàn)了time這個系統(tǒng)調(diào)用,替我們省掉了調(diào)用_syscall1宏展開得到函數(shù)原型這一步。

      大多數(shù)系統(tǒng)調(diào)用都在各種C語言函數(shù)庫中有所實現(xiàn),所以在一般情況下,我們都可以像調(diào)用普通的庫函數(shù)那樣調(diào)用系統(tǒng)調(diào)用,只在極個別的情況下,我們才有機會用到_syscall*()這幾個宏。





      回頁首


      _syscall*()是什么?

      在unistd.h里定義了7個宏,分別是

      _syscall0(type,name)
                  _syscall1(type,name,type1,arg1)
                  _syscall2(type,name,type1,arg1,type2,arg2)
                  _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)
                  _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)
                  _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5)
                  _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5,type6,arg6)
                  

      它們看起來似乎不太像宏,但其實質(zhì)和
      #define MAXSIZE 100
      里面的MAXSIZE沒有任何區(qū)別。

      它們的作用是形成相應(yīng)的系統(tǒng)調(diào)用函數(shù)原型,供我們在程序中調(diào)用。我們很容易就能發(fā)現(xiàn)規(guī)律,_syscall后面的數(shù)字和typeN,argN的數(shù)目一樣多。事實上,_syscall后面跟的數(shù)字指明了展開后形成函數(shù)的參數(shù)的個數(shù),讓我們看一個實例,就是剛剛用過的time系統(tǒng)調(diào)用:

      _syscall1(time_t,time,time_t *,tloc)

      展開后的情形是這樣:

      time_t   time(time_t *   tloc)
                  {
                  long __res;
                  __asm__ volatile("int $0x80" : "=a" (__res) : "0" (13),"b" ((long)(tloc)));
                  do {
                  if ((unsigned long)(__res) >= (unsigned long)(-125)) {
                  errno = -(__res);
                  __res  = -1;
                  }
                  return (time_t) (__res);
                  } while (0) ;
                  }

      可以看出,_syscall1(time_t,time,time_t *,tloc)展開成一個名為time的函數(shù),原參數(shù)time_t就是函數(shù)的返回類型,原參數(shù)time_t *和tloc分別構(gòu)成新函數(shù)的參數(shù)。事實上,程序中用到的time函數(shù)的原型就是它。





      回頁首


      errno是什么?

      為防止和正常的返回值混淆,系統(tǒng)調(diào)用并不直接返回錯誤碼,而是將錯誤碼放入一個名為errno的全局變量中。如果一個系統(tǒng)調(diào)用失敗,你可以讀出errno的值來確定問題所在。

      errno不同數(shù)值所代表的錯誤消息定義在errno.h中,你也可以通過命令"man 3 errno"來察看它們。

      需要注意的是,errno的值只在函數(shù)發(fā)生錯誤時設(shè)置,如果函數(shù)不發(fā)生錯誤,errno的值就無定義,并不會被置為0。另外,在處理errno前最好先把它的值存入另一個變量,因為在錯誤處理過程中,即使像printf()這樣的函數(shù)出錯時也會改變errno的值。





      回頁首


      系統(tǒng)調(diào)用兼容性好嗎?

      很遺憾,答案是--不好。但這決不意味著你的程序會三天兩頭的導(dǎo)致系統(tǒng)崩潰,因為系統(tǒng)調(diào)用是Linux的內(nèi)核提供的,所以它們工作起來非常穩(wěn)定,對于此點無需絲毫懷疑,在絕大多數(shù)的情況下,系統(tǒng)調(diào)用要比你自己編寫的代碼可靠而高效的多。

      但是,在Linux的各版本內(nèi)核之間,系統(tǒng)調(diào)用的兼容性表現(xiàn)得并不像想象那么好,這是由Linux本身的性質(zhì)決定的。Linux是一群程序設(shè)計高手利用業(yè)余時間開發(fā)出來的,他們中間的大部分人沒有把Linux當(dāng)成一個嚴肅的商業(yè)軟件,(現(xiàn)在的情況有些不同了,隨著Linux商業(yè)公司和以Linux為生的人的增長,不少人的腦筋發(fā)生了變化。)結(jié)果就是,如果新的方案在效率和兼容性上發(fā)生了矛盾,他們往往舍棄兼容性而追求效率,就這樣,如果他們認為某個系統(tǒng)調(diào)用實現(xiàn)的比較糟糕,他們就會毫不猶豫的作出修改,有些時候甚至連接口也一起改掉了,更可怕的是,很多時候,他們對自己的修改連個招呼也不打,在任何文檔里都找不到關(guān)于修改的提示。這樣,每當(dāng)新內(nèi)核推出的時候,很可能都會悄悄的更新一些系統(tǒng)調(diào)用,用戶編制的應(yīng)用程序也會跟著出錯。

      說到這里,你是不是感覺前途一片昏暗呢?呵呵,不用太緊張,如前面所說,隨著越來越多的人把Linux當(dāng)成自己的飯碗,不兼容的情況也越來越罕見。從2.2版本以后的Linux內(nèi)核已經(jīng)非常穩(wěn)定了,不過盡管如此,你還是有必要在每個新內(nèi)核推出之后,對自己的應(yīng)用程序進行兼容性測試,以防止意外的發(fā)生。





      回頁首


      該如何學(xué)習(xí)使用Linux系統(tǒng)調(diào)用呢?

      你可以用"man 2 系統(tǒng)調(diào)用名稱"的命令來查看各條系統(tǒng)調(diào)用的介紹,但這首先要求你要有不錯的英語基礎(chǔ),其次還得有一定的程序設(shè)計和系統(tǒng)編程的功底,man pages不會涉及太多的應(yīng)用細節(jié),因為它只是一個手冊而非教程。如果man pages所提供的東西不能使你感到非常滿意,那就跟我來吧,本專欄將向你展示Linux系統(tǒng)調(diào)用編程的無窮魅力。

      雖然本專欄并非異常高深的技術(shù)文章,但是還對讀者有兩點小小的要求:1)讀者必須有一定的C語言編程經(jīng)驗,本專欄不會在語言細節(jié)上過分糾纏;2)讀者必須有一定的Linux使用經(jīng)驗,本專欄也不打算在Linux應(yīng)用上大動干戈。舉一個小小的測試標準,如果你能完全看懂本文從開頭到這里所講的東西,你就合格了。收拾好行囊,準備出發(fā)吧!

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多