Perfacelinux
前面已經寫過一篇《嵌入式linux內核的五個子系統》,歸納性比較強,也比較簡略,如今對其進行補充說明。
編程
僅留此筆記,待往後查看及補充!
網絡
Linux內核的子系統 數據結構
內核是操做系統的核心。Linux內核提供不少基本功能,如虛擬內存、多任務、共享庫、需求加載、共享寫時拷貝(Copy-On-Write)以及網絡功能等。增長各類不一樣功能致使內核代碼不斷增長。socket
Linux內核把不一樣功能分紅不一樣的子系統的方法,經過一種總體的結構把各類功能集合在一塊兒,提升了工做效率。同時還提供動態加載模塊的方式,爲動態修改內核功能提供了靈活性。ide
系統調用接口函數
用戶程序經過軟件中斷後,調用系統內核提供的功能,這個在用戶空間和內核提供的服務之間的接口稱爲系統調用。單元測試
系統調用是Linux內核提供的,用戶空間沒法直接使用系統調用。在用戶進程使用系統調用必須跨越應用程序和內核的界限。Linux內核向用戶提供了統一的系統調用接口,可是在不一樣處理器上系統調用的方法各不相同。Linux內核提供了大量的系統調用,如今從系統調用的基本原理出發探究Linux系統調用的方法。 測試
這是在一個用戶進程中經過GNU C庫進行的系統調用示意圖,系統調用經過同一個入口點傳入內核。以i386體系結構爲例,約定使用EAX寄存器標記系統調用。
spa
當加載了系統C庫調用的索引和參數時,就會調用0x80軟件中斷,它將執行system_call函數,這個函數按照EAX寄存器內容的標示處理全部的系統調用。通過幾個單元測試,會使用EAX寄存器的內容的索引查system_call_table表獲得系統調用的入口,而後執行系統調用。從系統調用返回後,最終執行system_exit,並調用resume_userspace函數返回用戶空間。
linux內核系統調用的核心是系統多路分解表。最終經過EAX寄存器的系統調用標識和索引值從對應的系統調用表中查出對應系統調用的入口地址,而後執行系統調用。
linux系統調用並不單層的調用關係,有的系統調用會由內核進行屢次分解,例如socket調用,全部socket相關的系統調用都與_NR_socketcall系統調用關聯在一塊兒,經過另一個適當的參數得到適當的調用。
進程管理子系統
當用戶使用系統提供的庫函數進行進程編程,用戶能夠動態地建立進程,進程之間還有等待,互斥等操做,這些操做都是由linux內核來實現的。linux內核經過進程管理子系統實現了進程有關的操做,在linux系統上,全部的計算工做都是經過進程表現的,進程能夠是短時間的(執行一個命令),也能夠是長期的(一種網絡服務)。linux系統是一種動態系統,經過進程管理可以適應不斷變化的計算需求。
在用戶空間,進程是由進程標示符(PID)表示的。從用戶角度看,一個PID是一個數字值,能夠惟一標識一個進程,一個PID值在進程的整個生命週期中不會更改,可是PID能夠在進程銷燬後被從新使用。建立進程可使用幾種方式,能夠建立一個新的進程,也能夠建立當前進程的子進程。
在linux內核空間,每一個進程都有一個獨立的數據結構,用來保存該進程的ID、優先級、地址的空間等信息,這個結構也被稱作進程控制塊(Process Control Block)。所謂的進程管理就是對進程控制塊的管理。
linux的進程是經過fork()函數系統調用產生的。調用fork()的進程叫作父進程,生成的進程叫作子進程。子進程被建立的時候,除了進程ID外,其它數據結構與父進程徹底一致。在fork()系統調用建立內存以後,子進程立刻被加入內核的進程調試隊列,而後使用exec()系統調用,把程序的代碼加入到子進程的地址空間,以後子進程就開始執行本身的代碼。
在一個系統上能夠有多個進程,可是通常狀況下只有一個CPU,在同一個時刻只能有一個進程在工做,即便有多個CPU,也不可能和進程的數量同樣多。若是讓若干的進程都能在CPU上工做,這就是進程管理子系統的工做。linux內核設計了存放進程隊列的結構,在一個系統上會有若干隊列,分別存放不一樣狀態的進程。一個進程能夠有若干狀態,具體是由操做系統來定義的,可是至少包含運行態、就緒態和等待3種狀態,內核設計了對應的隊列存放對應狀態的進程控制塊。
當一個用戶進程被加載後,會進入就緒態,被加入到就緒態隊列,CPU時間被輪轉到就緒態隊列後,切換到進程的代碼,進程被執行,當進程的時間片到了之後被換出。若是進程發生I/O操做也會被提早被換出,而且存放到等待隊列,當I/O請求返回後,進程又被放入就緒隊列。
linux系統對進程隊列的管理設計了若干不一樣的方法,主要的目的是提升進程調試的穩定性。
內核管理子系統
內存是計算機的重要資源,也是內核的的重要部分。使用虛擬內存技術的計算機,內存管理的硬件按照分頁方式管理內存。分頁方式是把計算機系統的物理內存按照相同大小等分,每一個內存分片稱做內存頁,一般內存頁大小是4KB。Linux內核的內存管理子系統管理虛擬內存與物理內存之間的映射關係,以及系統可用內存空間。
內存管理要管理的不只是4KB緩衝區。Linux提供了對4KB緩衝區的抽象,例如slab分配器。這種內存管理模式使用4KB緩衝區爲基數,而後從中分配結構,並跟蹤內存頁使用狀況,好比哪些內存頁是滿的,哪些頁面沒有徹底使用,哪些頁面爲空。這樣就容許該模式根據系統須要來動態調整內存使用。
在支持多用戶的系統上,因爲內存佔用的增大,容易出現物理內存被消耗盡的狀況。爲了解決物理內存被耗盡的問題,內存管理子系統規定頁面能夠移出內存並放入磁盤中,這個過程稱爲交換。內存管理的源代碼能夠在./linux/mm中找到
虛擬文件系統
在不一樣格式的文件分區上,程序均可以正確地讀寫文件,而且結果是同樣的。有時在使用linux系統的時候發現,能夠在不一樣類型的文件分區內直接複製文件,對應用程序來講,並不知道文件系統的類型,甚至不知道文件的類型,這就是虛擬文件系統在背後作的工做。虛擬文件系統屏蔽了不一樣文件系統間的差別,向用戶提供了統一的接口。
虛擬文件系統,即VFS(Virtual File System)是Linux內核中的一個軟件抽象層。它經過一些數據結構及其方法向實際的文件系統如ext2,vfat等提供接口機制。經過使用同一套文件 I/O 系統調用便可對Linux中的任意文件進行操做而無需考慮其所在的具體文件系統格式;更進一步,文件操做能夠在不一樣文件系統之間進行。
在linux系統中,一切均可以被看作是文件。不只普通的文本文件、目錄能夠當作文件進行處理,並且字符設備、塊設備、套接字等均可以被當作文件進行處理。這些文件雖然類型不一樣,可是卻使用同一種操做方法。這也是UNIX/Linux設計的基本哲學之一。
虛擬文件系統(簡稱VFS)是實現「一切都是文件」特性的關鍵,是Linux內核的一個軟件層,向用戶空間的程序提供文件系統接口;同時提供了內核中的一個抽象功能,容許不一樣類型的文件系統存在。VFS能夠被理解爲一種抽象的接口標準,系統中全部的文件系統不只依靠VFS共存,也依靠VFS協同工做。
爲了可以支持不一樣的文件系統,VFS定義了全部文件系統都支持的、最基本的一個概念上的接口和數據結構,在實現一個具體的文件系統的時候,須要向VFS提供符合VFS標準的接口和數據結構,不一樣的文件系統可能在實體概念上有差異,可是使用VFS接口時須要和VFS定義的概念保持一致,只有這樣,才能實現對用戶的文件系統無關性。VFS隱藏了具體文件系統的操做細節,因此,在VFS這一層以及內核其餘部分看來,全部的文件系統都是相同的。
對文件系統訪問的系統調用經過VFS軟件層處理,VFS根據訪問的請求調用不一樣的文件系統驅動的函數處理用戶的請求。文件系統的代碼在訪問物理設備的時候,須要使用物理設備驅動訪問真正的硬件。
網絡堆棧
編寫網絡應用程序,使用socket經過TCP/IP協議與其餘機器通訊,和前面介紹的內核子系統類似,socket相關的函數也是經過內核的子系統完成的,擔當這部分任務的是內核的網絡子系統,有時也把這部分代碼稱爲「網絡堆棧」。
Linux內核提供了優秀的網絡處理能力和功能,這與網絡堆棧代碼的設計思想是分不開的,Linux的網絡堆棧部分沿襲了傳統的層次結構,網絡數據從用戶進程到達實際的網絡設備須要四個層次。
實際上,在每層裏面還能夠分爲好多層次,數據傳輸的路徑是按照層次來的,不能跨越某個層次。
linux網絡子系統對網絡層次採用了相似面向對象的設計思路,把須要處理的層次抽象爲不一樣的實體,而且定義了實體之間的關係和數據處理流程。
能夠看出,linux內核網絡子系統定義了4個實體:
❶網絡協議
網絡協議能夠理解爲一種語言,用於網絡中不一樣設備之間的通訊,是一種通訊的規範。
❷套接字
套接字是內核與用戶程序的接口,一個套接字對應一個數據鏈接,而且向用戶提供了文件I/O,用戶能夠像操做文件同樣在數據鏈接上收發數據,具體的協議處理由網絡協議部分處理。套接字是用戶使用網絡的接口。
❸設備接口
設備接口是網絡子系統中軟件和硬件的接口,用戶的數據最終是須要經過網絡硬件設備發送和接收的,網絡設備千差萬別,設備驅動也不盡相同,經過設備接口屏蔽了具體設備驅動的差別。
❹網絡緩衝區
網絡緩衝區也稱爲套接字緩衝區(sk_buff),是網絡子系統中的一個重要結構。網絡傳輸數據存在許多不定因素,除了物理設備對傳輸數據的限制(例如MMU),網絡受到干擾、丟包、重傳等,都會形成數據的不穩定,網絡緩衝區經過對網絡數據的從新整理,使業務處理的數據包是完整的。網絡緩衝區是內存中的一塊緩衝區,是網絡系統與內存管理的接口。
設備驅動
隨着現代計算機外部設備的不斷增長,起來越多的設備被開發出來,計算機總線的發展也很迅速,操做系統的功能也在不斷提高,系統軟件愈來愈複雜,對於外部設備的訪問已經不能像DOS年代那樣直接訪問設備的硬件了,幾乎全部的設備都須要設備驅動程序。現代操做系統幾乎都提供了具體硬件無關的設備驅動接口,這樣的好處是屏蔽了具體設備的操做細節,用戶經過操做系統提供的接口就能夠訪問設備,而具體設備的操做細節由設備驅動完成,驅動程序開發人員只須要向操做系統提供相應接口便可。
與其餘的操做系統對設備進行復雜的分類不一樣,linux內核把設備分紅3類:塊設備、字符設備和網絡設備。這是一種抽象的分類方法,從設備的特性抽象出了3種不一樣的數據讀寫方式。
❶塊設備
塊設備的概念是一次I/O操做能夠操做多個字節的數據,數據讀寫有緩衝,當讀寫緩衝滿之後纔會傳送數據,好比硬盤能夠一次讀取一個扇區的數據,同時,塊設備支持隨機讀寫操做,能夠從指定的位置讀寫數據;
❷字符設備
字符設備的訪問方式是線性的,而且能夠按照字節的方式訪問,好比串口設備,能夠按照字符讀寫數據,可是隻能按照順序操做,不能指定某個地址訪問;
❸網絡設備
網絡設備與前面的兩種方式相比,比較特殊,內核專門把這類驅動單獨劃分出來,網絡設備能夠經過套接口讀寫數據。
Linux內核對設備按照主設備號和從設備號的方法訪問,主設備號描述控制設備的驅動程序,從設備號區分同一個驅動程序的不一樣設備。也就是說,主設備號和設備驅動程序對應,表明某一類型的設備,從設備號和具體設備對應,表明同一類的設備編號。如使用IDE接口的兩個硬盤,主設備號都相同,可是從設備號不一樣。Linux提供了mknod命令建立設備驅動程序的描述文件。Linux內核這種主從設備號的分類方法能夠很好的管理設備。
上圖爲用戶程序從外部設備請求數據的流程,能夠看出,用戶進程訪問外部設備是經過設備無關軟件進行的,設備無關軟件是內核中的各類軟件抽象層如VFS。當用戶向外部設備發起數據請求時,經過設備無關軟件會調用設備的相應驅動程序,驅動程序經過總線或者寄存器訪問外部硬件設備,發起請求,驅動程序會在初始化的時候向系統的中斷向量表註冊一箇中斷處理程序,外部硬件有請求返回的時候會發出中斷信號,內核會調用響應的中斷處理程序,中斷處理程序從硬件的寄存器讀取返回的數據,而後轉交給內核中的設備服務程序,由設備服務程序把數據交給設備無關的軟件,最終到達用戶進程。
linux的設備驅動涉及其餘子系統,如內存管理、中斷管理、硬件寄存器和總線訪問等,此外,大多數的驅動程序爲了使用方便被設計成模塊,還須要設計到內核模塊的處理。
驅動的編寫和調試是一個複雜的事情,驅動的代碼佔用了linux內核代碼量的一半以上。
依賴體系結構的代碼
Linux內核支持衆多體系結構,內核把與設備無關的代碼放在arch目錄,對應的頭文件放在include/asm-<體系名稱>目錄下。這樣的劃分代碼結構清晰,同時提升了代碼的複用率。在arch目錄裏,每一個子目錄對應一種體系結構,存放這種體系結構對應的代碼,若是代碼較多會單獨創建一個目錄,例如arch/arm目錄下,有一個kernel目錄,存放的是kernel目錄中在arm體系結構上特有的函數或者實現方法;在arch/i386目錄存放了Intel X86體系結構的代碼,不只有kernel目錄,並且還有多個目錄,例如mm目錄包含了x86體系上內存管理的實現方法,math-emu包含了x86體系上浮點數模擬的實現等。讀者在閱讀內核代碼的時候能夠從一個體繫結構代碼入手,對不一樣體系結構移植代碼的主要工做是arch裏面的代碼。