【Linux技術】探究linux內核,超詳細解析子系統

Perface


   前面已經寫過一篇嵌入式linux內核的五個子系統,概括性比較強,也比較簡略,現在對其進行補充說明。

   僅留此筆記,待日後查看及補充!



Linux內核的子系統


   內核是操作系統的核心。Linux內核提供很多基本功能,如虛擬內存、多任務、共享庫、需求加載、共享寫時拷貝(Copy-On-Write)以及網絡功能等。增加各種不同功能導致內核代碼不斷增加。

   Linux內核把不同功能分成不同的子系統的方法,通過一種整體的結構把各種功能集合在一起,提高了工作效率。同時還提供動態加載模塊的方式,爲動態修改內核功能提供了靈活性。



系統調用接口


   用戶程序通過軟件中斷後,調用系統內核提供的功能,這個在用戶空間和內核提供的服務之間的接口稱爲系統調用。

650) this.width=650;" src="http://img1.51cto.com/attachment/201305/112326483.png" title="linux內核功能結構圖.png" />


   系統調用是Linux內核提供的,用戶空間無法直接使用系統調用。在用戶進程使用系統調用必須跨越應用程序和內核的界限。Linux內核向用戶提供了統一的系統調用接口,但是在不同處理器上系統調用的方法各不相同。Linux內核提供了大量的系統調用,現在從系統調用的基本原理出發探究Linux系統調用的方法。

650) this.width=650;" src="http://img1.51cto.com/attachment/201305/112714621.jpg" title="使用中斷方法的系統調用過程.jpg" />


   這是在一個用戶進程中通過GNU C庫進行的系統調用示意圖,系統調用通過同一個入口點傳入內核。以i386體系結構爲例,約定使用EAX寄存器標記系統調用。

   當加載了系統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中的任意文件進行操作而無需考慮其所在的具體文件系統格式;更進一步,文件操作可以在不同文件系統之間進行。

650) this.width=650;" src="http://img1.51cto.com/attachment/201305/121039243.jpg" title="內核中VFS與實際文件系統的關係.jpg" />

   在linux系統中,一切都可以被看做是文件。不僅普通的文本文件、目錄可以當做文件進行處理,而且字符設備、塊設備、套接字等都可以被當做文件進行處理。這些文件雖然類型不同,但是卻使用同一種操作方法。這也是UNIX/Linux設計的基本哲學之一。

   虛擬文件系統(簡稱VFS)是實現“一切都是文件”特性的關鍵,是Linux內核的一個軟件層,向用戶空間的程序提供文件系統接口;同時提供了內核中的一個抽象功能,允許不同類型的文件系統存在。VFS可以被理解爲一種抽象的接口標準,系統中所有的文件系統不僅依靠VFS共存,也依靠VFS協同工作。

   爲了能夠支持不同的文件系統,VFS定義了所有文件系統都支持的、最基本的一個概念上的接口和數據結構在實現一個具體的文件系統的時候,需要向VFS提供符合VFS標準的接口和數據結構,不同的文件系統可能在實體概念上有差別,但是使用VFS接口時需要和VFS定義的概念保持一致,只有這樣,才能實現對用戶的文件系統無關性。VFS隱藏了具體文件系統的操作細節,所以,在VFS這一層以及內核其他部分看來,所有的文件系統都是相同的。

   對文件系統訪問的系統調用通過VFS軟件層處理,VFS根據訪問的請求調用不同的文件系統驅動的函數處理用戶的請求。文件系統的代碼在訪問物理設備的時候,需要使用物理設備驅動訪問真正的硬件。



網絡堆棧


   編寫網絡應用程序,使用socket通過TCP/IP協議與其他機器通信,和前面介紹的內核子系統相似,socket相關的函數也是通過內核的子系統完成的,擔當這部分任務的是內核的網絡子系統,有時也把這部分代碼稱爲“網絡堆棧”。

   Linux內核提供了優秀的網絡處理能力和功能,這與網絡堆棧代碼的設計思想是分不開的,Linux的網絡堆棧部分沿襲了傳統的層次結構,網絡數據從用戶進程到達實際的網絡設備需要四個層次。

650) this.width=650;" src="http://img1.51cto.com/attachment/201305/123533585.jpg" title="linux內核網絡子系統層次結構.jpg" />

   實際上,在每層裏面還可以分爲好多層次,數據傳輸的路徑是按照層次來的,不能跨越某個層次。

   linux網絡子系統對網絡層次採用了類似面向對象的設計思路,把需要處理的層次抽象爲不同的實體,並且定義了實體之間的關係和數據處理流程。

650) this.width=650;" src="http://img1.51cto.com/attachment/201305/123758759.jpg" title="linux內核網絡子系統各實體關係.jpg" />

   可以看出,linux內核網絡子系統定義了4個實體:

   ❶網絡協議

   網絡協議可以理解爲一種語言,用於網絡中不同設備之間的通信,是一種通信的規範。

   ❷套接字

   套接字是內核與用戶程序的接口,一個套接字對應一個數據連接,並且向用戶提供了文件I/O,用戶可以像操作文件一樣在數據連接上收發數據,具體的協議處理由網絡協議部分處理。套接字是用戶使用網絡的接口。

    ❸設備接口

   設備接口是網絡子系統中軟件和硬件的接口,用戶的數據最終是需要通過網絡硬件設備發送和接收的,網絡設備千差萬別,設備驅動也不盡相同,通過設備接口屏蔽了具體設備驅動的差異。

   ❹網絡緩衝區

   網絡緩衝區也稱爲套接字緩衝區(sk_buff),是網絡子系統中的一個重要結構。網絡傳輸數據存在許多不定因素,除了物理設備對傳輸數據的限制(例如MMU),網絡受到干擾、丟包、重傳等,都會造成數據的不穩定,網絡緩衝區通過對網絡數據的重新整理,使業務處理的數據包是完整的。網絡緩衝區是內存中的一塊緩衝區,是網絡系統與內存管理的接口。



設備驅動


   隨着現代計算機外部設備的不斷增加,起來越多的設備被開發出來,計算機總線的發展也很迅速,操作系統的功能也在不斷提升,系統軟件越來越複雜,對於外部設備的訪問已經不能像DOS年代那樣直接訪問設備的硬件了,幾乎所有的設備都需要設備驅動程序。現代操作系統幾乎都提供了具體硬件無關的設備驅動接口,這樣的好處是屏蔽了具體設備的操作細節,用戶通過操作系統提供的接口就可以訪問設備,而具體設備的操作細節由設備驅動完成,驅動程序開發人員只需要向操作系統提供相應接口即可。

   與其他的操作系統對設備進行復雜的分類不同,linux內核把設備分成3類:塊設備、字符設備和網絡設備。這是一種抽象的分類方法,從設備的特性抽象出了3種不同的數據讀寫方式。

   ❶塊設備

   塊設備的概念是一次I/O操作可以操作多個字節的數據,數據讀寫有緩衝,當讀寫緩衝滿以後纔會傳送數據,比如硬盤可以一次讀取一個扇區的數據,同時,塊設備支持隨機讀寫操作,可以從指定的位置讀寫數據;

   ❷字符設備

   字符設備的訪問方式是線性的,並且可以按照字節的方式訪問,比如串口設備,可以按照字符讀寫數據,但是隻能按照順序操作,不能指定某個地址訪問;

   ❸網絡設備

   網絡設備與前面的兩種方式相比,比較特殊,內核專門把這類驅動單獨劃分出來,網絡設備可以通過套接口讀寫數據。

   Linux內核對設備按照主設備號和從設備號的方法訪問,主設備號描述控制設備的驅動程序,從設備號區分同一個驅動程序的不同設備。也就是說,主設備號和設備驅動程序對應,代表某一類型的設備,從設備號和具體設備對應,代表同一類的設備編號。如使用IDE接口的兩個硬盤,主設備號都相同,但是從設備號不同。Linux提供了mknod命令創建設備驅動程序的描述文件。Linux內核這種主從設備號的分類方法可以很好的管理設備。

650) this.width=650;" src="http://img1.51cto.com/attachment/201305/125856993.jpg" title="linux內核驅動程序示意圖.jpg" />


   上圖爲用戶程序從外部設備請求數據的流程,可以看出,用戶進程訪問外部設備是通過設備無關軟件進行的,設備無關軟件是內核中的各種軟件抽象層如VFS。當用戶向外部設備發起數據請求時,通過設備無關軟件會調用設備的相應驅動程序,驅動程序通過總線或者寄存器訪問外部硬件設備,發起請求,驅動程序會在初始化的時候向系統的中斷向量表註冊一箇中斷處理程序,外部硬件有請求返回的時候會發出中斷信號,內核會調用響應的中斷處理程序,中斷處理程序從硬件的寄存器讀取返回的數據,然後轉交給內核中的設備服務程序,由設備服務程序把數據交給設備無關的軟件,最終到達用戶進程。

   linux的設備驅動涉及其他子系統,如內存管理、中斷管理、硬件寄存器和總線訪問等,此外,大多數的驅動程序爲了使用方便被設計成模塊,還需要設計到內核模塊的處理。

   驅動的編寫和調試是一個複雜的事情,驅動的代碼佔用了linux內核代碼量的一半以上。



依賴體系結構的代碼


   Linux內核支持衆多體系結構,內核把與設備無關的代碼放在arch目錄,對應的頭文件放在include/asm-<體系名稱>目錄下。這樣的劃分代碼結構清晰,同時提高了代碼的複用率。在arch目錄裏,每個子目錄對應一種體系結構,存放這種體系結構對應的代碼,如果代碼較多會單獨建立一個目錄,例如arch/arm目錄下,有一個kernel目錄,存放的是kernel目錄中在arm體系結構上特有的函數或者實現方法;在arch/i386目錄存放了Intel X86體系結構的代碼,不僅有kernel目錄,而且還有多個目錄,例如mm目錄包含了x86體系上內存管理的實現方法,math-emu包含了x86體系上浮點數模擬的實現等。讀者在閱讀內核代碼的時候可以從一個體繫結構代碼入手,對不同體系結構移植代碼的主要工作是arch裏面的代碼。



本文出自 “成鵬致遠” 博客,請務必保留此出處http://infohacker.blog.51cto.com/6751239/1203700

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章