深刻理解Linux內核(1)---基本概念

 1.Linux與其餘著名的商用UNIX比較node

單塊結構的內核(monolithic kernel 程序員

它是一個龐大的,複雜的自我完善(do-it-yourself)程序,由幾個邏輯上獨立的成分構成。這點上是至關傳統的,大多商用UNIX也是單塊結構,但另外的是appleMac Os XGNUHurd,用的微內核的方法算法

編譯並靜態連接的傳統Unix內核 數組

能夠動態安裝和卸載部份內核代碼(好比驅動)緩存

內核線程 bash

內核線程是一個能被獨立調度的執行環境(context),線程之間切換比普通進程的上下文切換話費代價少量多,由於線程在同一個地址空間執行。內核線程能夠跟應用程序有關,也能夠無關。網絡

支持多線程 session

Linux定義了本身的輕量級進程版本,商用unix變體都基於內核線程,而Linux吧輕量級進程看成基本的執行上下文,經過非標準的clone()系統調用來處理它們。數據結構

搶佔式(preemptive)內核 多線程

多處理器支持

Linux 2.6以幾乎最優化的方式使用SMP

文件系統

支持Ext2,Ext3jfs

STREAMS

大部分Unix內核包含了SRV4引入的STREAM I/O子系統,而且已變成編寫驅動,網絡協議的首選接口,但Linux沒有此類子系統

2.Linux與商業Unix競爭的優點

Linux是免費的

Linux的全部成分均可以充分的定製

Linux能夠運行在低檔、便宜的意見平臺上

Linux是強大的

Linux開發者都是很是出色的程序員

Linux內核很是小,並且緊湊

Linux與不少通用操做系統高度兼容

Linux有很好的技術支持

3.基本概念

操做系統必須完成兩個主要目標:管理全部硬件,爲全部app提供運行環境。Linux分用戶態(User Mode)和內核態(Kernel Mode,以免用戶直接操做硬件。

⑴多用戶系統(multiusers system

多用戶系統就是一臺能併發(concurrently)和獨立(independently)運行若干app的計算機。

併發:多個app能夠競爭各類資源,如CPU,內存,硬盤等

獨立:應用執行本身的任務,與其餘應用無關。

多用戶系統必須包含如下特色:

覈實用戶身份的認知機制

防止有錯誤的app妨礙其餘app在系統運行的保護機制

防止有惡意的app干涉或窺視其餘用戶的活動的保護機制

限制分配給每一個用戶的資源數的計賬機制

Unix是實行系統資源硬件保護的多用戶系統

(2)用戶和組

在多用戶系統中,每一個用戶在機器上都有私用空間,私有的權限。特別是必須保證,沒有用戶可以開發一個侵犯其餘用戶私有空間的系統app.

每一個用戶都有惟一的用戶標誌符(User ID,UID),隸屬於一個或多個組的一名成員,組由惟一的用戶組標誌符(user group ID)標識,每一個文件都屬於特定的用戶和組,能夠設置其相應權限。

Root用戶擁有一切權利,幾乎無所不能,能夠訪問任何文件,能夠干涉任何正在執行的用戶程序活動。

(3)進程

資源分配的基本單位,多道程序系統(multiprogramming)或多處理系統(multiprocessing)同時執行的進程數是有限的,由調度程序(scheduler)決定那個進程執行。

多用戶系統必須是可搶佔式的(preemptable),操做系統記錄下每一個進程佔有CPU時間,並週期性地激活調度程序。

Unix是具備搶佔式的多處理操做系統,即便沒有用戶登陸,沒有程序運行,也有幾個系統進程在監視外圍設備,好比監聽終端等待用戶登陸。

Unix操做系統採用進程/內核模式,每一個進程都自認爲是系統中惟一的進程,能夠獨佔OS所提供的服務,只要進程發出系統調用,硬件就會把特權模式由用戶態變成內核態,而後進程以很是有限的目的開始一個內核過程的執行,這樣,操做系統在進程的執行上下文中做用,以知足進程請求,一旦這個請求獲得知足,內核過程將迫使硬件返回用戶態,而後進程在系統調用的下一條指令繼續執行。

(4)內核體系結構

大部分Unix內核是單塊結構:每個內核層都被集成到整個內核程序中,並表明當前進程的內核態下運行。相反,微內核(microkernel)只須要內核有一個很小的函數集,它要求程序員採用模塊化的方法,由於任何操做系統層都是一個相對獨立的程序,這種程序必須經過清晰明確的軟件接口與其餘層交互,容易移植,且能比單塊內核更充分利用RAM,由於暫且不須要執行的系統進程能夠被調出或撤銷,但微內核通常比單塊內核效率低,由於操做系統不一樣層次之間顯示的消息傳遞要花費必定的代價。

爲了達到微內核理論上的不少優勢又不影響性能,Linux內核提供了模塊(module)。模塊是一個目標文件,其代碼能夠運行時鏈接到內核或卸載,一般用來實現文件系統,驅動程序或其餘內核上層功能。與微內核操做系統的外層不一樣,模塊不是做爲一個特殊的進程執行的,相反,與任何其餘靜態連接的內核函數同樣,它表明當前進程在內核態下執行。

使用模塊化主要優勢包括:

①模塊化方法:能夠在運行時動態加載或卸載,所以系統程序員必須提供良好定義的軟件接口以訪問由模塊處理的數據結構,這使得開發新模塊變得容易。

②平臺無關性:即便模塊依賴於某些特殊的硬件特色,但它不依賴於某個固定的硬件平臺

③節省內存使用:當須要模塊功能時,把它連接到運行的內核中,不然將該模塊解除,這種機制對於小型嵌入式系統很是有用

④無性能損失:模塊的目標代碼一旦被連接到內核,其做用與靜態連接的內核的目標代碼徹底等價,所以當模塊函數被調用時,無需顯示地進行消息傳遞。

(5)文件

Unix文件是以字節序列組成的信息載體(container),內核不解釋文件的內容,不少庫函數實現了更高級的抽象,例如由字段構成的記錄,從用戶觀點來看,文件被組織在一個樹結構的命名空間中,

除了葉節點外,樹的全部節點都表示目錄名,目錄節點包含它下面文件及目錄的全部信息。文件和目錄名由處」/」和空字符」\0」以外的任意ASCII字符序列組成,大多數文件系統對文件名長度都有限制,一般不超過255字符。

Unix的每一個進程都有一個當前工做目錄,它屬於進程執行上下文(execution context),標誌出進程所用的當前目錄。分絕對路徑和相對路徑。

(6)硬連接和軟鏈接

包含在目錄中的文件名就是一個文件的硬連接(hard link),或簡稱連接(link),在同一目錄或不一樣目錄中,同一文件能夠有幾個連接,所以對應幾個文件名。

$ ln p1 p2

用來建立一個新的硬連接,即爲路徑p1標識的文件建立一個路徑爲p2的硬連接。

硬連接有兩個限制:

①不容許給目錄建立硬連接,防止把目錄樹變爲環形圖

②只有在同一個文件系統的文件之間才能建立連接。

 

這帶來很大限制,由於現代Unix系統可能包含多種文件系統,這些文件系統位於不一樣的磁盤或分區,用戶沒法知道他們之間的物理劃分,爲了克服這些限制,所以引入了軟鏈接(soft link)也叫符號連接(symbol link),符號連接是短文件,能夠指向任何路徑名,甚至指向不存在文件。

$ ln –s p1 p2

建立一個路徑名爲P2的新軟連接,指向p1

(7)文件類型

Unix文件能夠是下列類型之一:

普通文件(regular file)

目錄

符號連接

面向快的設備文件(block-oriented device file)

面向字符的設備文件(character-oriented device file)

管道(pipe)和命名管道(named pipe)(也叫FIFO

套接字(socket)

前三個是Unix文件系統的基本類型,設備文件與驅動程序相關,訪問文件時即直接訪問I/O設備,管道和套接字是用於進程間通訊的特殊文件。

 

(8)文件描述符與索引節點

Unix對文件的內容描述和描述文件的信息給出了清楚的區分,除了設備文件和特殊文件外,每一個文件都有字符序列組成,文件內容不包含任何控制信息,如文件長度,權限等。

文件系統處理文件須要的全部信息包含在一個名爲索引節點(inode)的數據結構中,每一個文件都有本身的索引節點,文件系統用索引節點來標誌文件。

雖然文件系統及內核函數對索引節點的處理可能隨Unixixt不一樣有所不一樣,但通常都必須至少提供在POSIX標準中指定的以下屬性:

文件類型

與文件相關的硬連接個數

以字節爲單位的文件長度

設備標誌符

在文件系統中標誌文件的索引節點號

文件擁有者的UID

文件的用戶組ID

幾個時間戳,表示索引節點改變時間、最後訪問時間及最後修改時間

訪問權限和文件模式

(9)訪問權限和文件模式

文件的潛在用戶分爲三種類型

文件擁有者用戶

同組用戶,不包括全部者

其餘用戶

有三種訪問權限讀、寫及執行。還有三種附加的標記

Suid:(set User ID)進程執行一個文件時一般保持進程擁有者UID,若是設置了可執行文件的suid標誌位,進程就得到了該文件擁有者的UID

Sgid(Set Group ID):進程執行一個文件時保持進程組的組ID,若是設置了可執行文件的sgid標誌位,進程就得到了該文件用戶組的ID

Sticky:

當文件由一個進程建立時,文件擁有者ID就是該進程UID,而其組用戶ID能夠是進程建立者的ID,也能夠是父目錄的ID,取決於副目錄的sgid標誌位

(10)文件操做的系統調用

當用戶訪問一個普通文件或目錄內容時,他其實是訪問存儲在硬件塊設備上的一些數據,從這個意義上說,文件系統是硬盤分區物理組織的用戶及視圖。

①打開文件 fd= open(path,flag,mode)

這個系統調用建立一個「打開文件」對象,並返回文件描述符(file descriptor).

這個對象包括:

文件操做的一些數據結構,如打開方式,偏移等/

進程能夠調用的一些內核函數指針

文件描述符表示進程與打開文件之間的交互,而打開文件對象包含了與這種交互相關的數據。同一個文件對象也許由幾個文件描述符標誌,而且Unix文件系統在同一文件上發出的I/O操做之間不提供任何形式的同步機制。

②訪問文件

Lseek,read,write,close(fd)

③改名及刪除

Res = rename(oldpath,newpath),改變了文件連接的名字

Res=unlink(pathname),減小文件鏈接數,刪除了相應目錄項,當鏈接數爲0時,文件才被真正刪除

 

11)進程/內核模式

CPU既能夠運行在用戶態,也能夠運行在內核態下,實際上一些CPU能夠運行兩種以上執行狀態,如Intel80x86微處理器有四種不一樣的執行狀態,ARM有其中執行狀態,但全部標準的Unix內核都僅僅利用了內核態和用戶態。

進程是動態的屍體,在系統內一般只有有限的生命期。

內核自己並非一個進程,而是進程的管理者。

 

除用戶進程外,Unix系統還包括幾個內核線程(kernel thread)的特權進程,它們有如下特色:

它們之內核態運行在內核地址空間

他們不與用戶直接交互,所以不須要終端設備

他們一般在系統啓動時建立,而後一直處於活躍狀態知道系統關閉

Unix內核的工做遠不止處理系統調用,實際有4中方式激活內核例程

①進程調用系統調用

CPU發出異常(exception)信號

③外圍向CPU發送中斷(interrupt)信號

④內核線程被執行。

 

爲了讓內核管理進程,每一個進程由一個進程描述符(process description)表示,這個進程描述符包含有關進程當前狀態的信息。

當內核暫停一個進程執行時,就把幾個相關寄存器內容保存進進程描述符(PC,SP,通用寄存器,浮點寄存器,處理器狀態字),當內核決定恢復執行一個進程時,用進程描述符中合適的字段來裝載CPU寄存器。

(12)可重入內核

全部的Unix內核都是可重入的(reentrant,這意味着若干進程能夠同時在內核下執行,提供可重入的一種方式是,編寫內核函數只改變局部變量不改變全局結構,儘管如此(一些實時內核就是如此實現),可重入內核能夠包括非重入函數,而且利用鎖機制保證一次只有一個進程執行的非重入函數。

內核控制路徑(kernel control path):表示內核處理系統調用、異常或中斷所執行的質量序列。

當下屬事件之一發生時,CPU交錯執行內核控制路徑:

①進程調用一個系統調用,相應內核控制路徑證明該請求沒法當即知足,內核控制路徑調用調度程序選擇一個新的進程投入運行,結果進程切換髮生,第一個內核控制路徑沒完成,而CPU又從新開始執行其餘內核控制路徑,這種狀況下,兩條控制路徑表明兩個不一樣的進程在執行。

②當運行一個內核控制路徑時,CPU檢測到一個異常

③當CPU正在運行一個啓動了中斷的內核控制路徑,硬件中斷髮生

④在支持搶佔式調度的內核中,CPU正在運行,而一個更高優先級的進程加入就緒隊列,則中斷髮生。

 

(13)進程地址空間

每一個進程運行在它的私有地址空間,在用戶態下運行的進程設計有私有棧,數據區和代碼區,當在內核態運行時,進程訪問內核的數據區和代碼區,可是用另外私有的棧。

由於內核是可重入的,所以幾個內核控制路徑(對應不一樣進程相關)能夠輪流執行,這種狀況下,每一個內核控制路徑都引用它本身的私有內核棧。

儘管看起來每一個進程訪問一個私有地址空間,但有時進程之間也共享部分地址空間。

①用戶空間的進程間內存共享。

②同一個程序由幾個用戶同時使用,則這個程序只被裝入內存一次,其指令由全部須要它的用戶共享。

mmap()系統調用,容許塊設備上文件映射到進程的部分地址空間。

 

14)同步和臨界區

當調度多個進程訪問共享數據時,存在一種競爭條件(race condition),實現可重入內核須要利用同步機制。

①非搶佔式內核

在尋找完全、簡單地解決同步問題的方案中,大多數傳統的Unix內核都是非搶佔式的。

若是內核支持搶佔,那麼在應用同步機制時,確保進入臨界區前禁止搶佔,退出臨界區時開啓搶佔。

②禁止中斷

單處理器系統上,在進入臨界區時,禁止全部硬件中斷,離開臨界區時再重啓中斷。

但在多處理器系統中禁止本地CPU中斷時不夠的,全部仍是要採用其餘的同步技術。

③信號量

普遍使用的一種機制是信號量(semaphore),它在單處理器,多處理器系統中都有效。能夠把信號量看做一個對象,它包括:

一個整數變量

一個等待進程的鏈表

兩個原子方法:down()和up()

每一個要保護的數據結構都有它本身的信號量,其初始值爲1.down()方法對信號量值減1,up()對信號量值加1,若信號量值小於0,則加入到等待鏈表中,且當前進程掛起,若大於0,則正常訪問數據。

④自旋鎖

在多處理系統中,信號量並不老是解決同步的最好辦法。自旋鎖與信號量相似,但沒有鏈表,當一個進程發現鎖被另外一個進程持有時,它不停的「旋轉」,執行一個緊湊的循環指令制導鎖打開。

在單處理器環境下,自旋鎖是無效的,由於當一個進程等待鎖時,永遠得不到鎖釋放,系統將掛起。

⑤避免死鎖

只要涉及到內核設計,當所用內核信號量數量較多時,死鎖就成爲一個突出問題。Linux經過按規定的循序請求信號量來避免死鎖,例如哲學家就餐問題。

(15)信號與進程間通訊

Unix信號(signal)提供了把系統事件報告給進程的一種機制,有兩種系統事件:

異步通告---例如當用戶在終端按下中斷建(CTRL+C,即向前臺發送一箇中斷信號SIGINT

同步錯誤或異常---例如當進程訪問非法內存地址時,內核向這個進程發送一個SIGSEGV信號

通常來講,進程能夠有兩種方式對信號作出反應:忽略該信號,異步地執行一個指定過程(信號處理函數)

POSIX標準定義了大約20種不一樣信號,其中有2個是用戶自定義的。

 

若是進程不指定選擇何種方式,內核就根據信號的編號執行一個默認操做,5種可能的默認操做是:

①終止進程

②將執行上下文或進程地址空間內容寫入一個文件(核心轉儲,core dump)並終止進程

③忽略信號

④掛起進程

⑤若是進程曾被暫停,則恢復它的執行。

AT&TUnix System V引入了在用戶態下進程間通訊機制:信號量,消息隊列及共享內存,統稱爲System V IPC。共享內存是進程間交換和共享數據的最快方式。

16)進程管理

Unix在進程和正在執行的程序之間作出了清晰劃分。Fork()_exit()分別用來建立和終止一個進程,而exec()類系統調用則是裝入一個新程序,該新程序替換了當前進程的正文、數據、堆和棧段,進程ID不變。

實現fork()是將父進程數據和代碼徹底複製,這會至關費時,當前以來硬件分頁單元的內核採用寫時複製(Copy-On-Write)技術,及把頁的複製延遲到最後一刻(子進程須要時才寫進頁)

_exit()系統調用終止一個進程,內核對這個系統調用處理是釋放內核擁有的資源並向父進程發送SIGCHILD信號(默認操做爲忽略)

17)殭屍進程(zombie precess)

父進程調用wait4()等待其中一個子進程結束,它返回已終止子進程的進程標誌符(Precess IDPID),若是沒有子進程退出,該進程就設置成等待狀態,一直到子進程結束。

若在父進程調用wait4()以前,子進程就死掉了,則成爲僵死進程。

解決方法是init進程,它在系統初始化時建立,當一個進程終止時,內核改變其全部現有子進程的進程描述符指針,收養全部這些進程爲init的子進程。Init監控全部子進程的執行,而且按常規發佈wait4(),這樣能夠除掉全部殭屍進程。

18)進程組和登錄會話

現代Unix引入進程組(process group)的概念,以表示一種做業(job)的抽象。

$ls |sort |more

Shell支持進程組,例如bash,爲三個相應進程ls,sort,more建立一個進程組,每一個進程組有一個領頭進程(其PID與進程組ID相同的進程),新建立的進程最初被插入到父進程的進程組中。

登錄會話(login session):一個登錄會話包含指定終端已經開始工做會話的那個進程的全部後代進程。進程組中的全部進程必須在同一個登錄會話中。只有一個進程組一直處於前臺,它能夠訪問中斷,其餘活動着的進程組在後臺,後臺進程訪問中斷時,將受到SIGTTINSIGTTOUT信號,能夠用bgfg把一個進程放在後臺或前臺。

19)內存管理

①虛擬內存(virtual memory

虛擬內存有多個有點和用途:

若干個進程能夠併發地執行

應用程序所需內存大於可用物理內存時也能夠運行

程序只有部分代碼裝入內存時,進程也能夠執行它

容許每一個進程訪問可用物理內存的子集

進程能夠共享庫函數或程序的一個單獨內存映像

程序能夠重定位,便可以在任何地址執行

程序員能夠編寫機器無關代碼

如今的CPU包含了能自動把虛擬地址轉換成物理地址的硬件電路,用頁表來指定虛擬地址和物理地址的對應關係。

②隨機訪問存儲器(RAM)的使用

內核把RAM分爲兩個部分,一部分專門用於放內核映像,其餘部分由虛擬內存系統來處理。

虛擬內存系統必須解決的一個主要問題是內存碎片。

③內核內存分配器(Kernel Memory AllocatorKMA

它試圖知足系統中全部部分對內存的請求,一個好的KMA應具備下列特色:

必須快

必須把內存的浪費減到最小

必須努力減輕內存的碎片問題(fragmentation

必須能與其餘內存管理子系統合做,以便借用和釋放頁框。

基於各類不一樣的算法技術,已經提出了幾種KMA,包括

資源分配算法(allocator

2的冪次方空閒鏈表

McKusick-Karels分配算法

夥伴(Buddy)系統

Mach的區域(Zone)分配算法

Dynix分配算法

SolarisSlab分配算法

④進程虛擬地址空間處理

內核分配給進程的虛擬地址空間由如下內存組成:

程序的可執行代碼

程序的初始化數據

程序的未初始化數據

初始程度棧(即用戶態棧)

所需共享庫的可執行代碼和數據

堆(用於程序動態請求內存)

全部現代Unix操做系統採用了請求調頁(demand paging)的內存分配策略。

⑤高速緩存

最先在Unix系統實現的一個策略是,儘量地推遲寫磁盤的時間,從磁盤讀入內存的數據集市進程再也不使用它們,他們也留在RAM中,當一個進程請求訪問磁盤時,內核首先檢查所請求數據是否在內存中,若在(叫作緩存命中),內核就不用訪問磁盤了。

Sync()系統調用把全部「髒」的緩衝區(緩衝與磁盤內容不同),寫入磁盤來強制磁盤同步。

20)設備驅動程序

設備驅動包含在內核中,由一個或多個設備的數據結構和函數組成,經過特定的接口,每一個驅動程序與內核的其他部分相互做用。

相關文章
相關標籤/搜索