理解linux內核的意義,基于深入理解Linux內核 第三版 陳莉俊譯
任何計算機系統都包含一個名為操作系統的基本程序集合。在這個集合里,最重要的程序稱為內核(Kernel)
。當操作系統啟動時,內核被裝入到RAM中,內核中包含了系統運行所必不可少的很多核心過程(procedure)。
操作系統必須完成主要的兩個重要目標:
UNIX/LINUX?類Unix操作系統把與計算機物理組織相關的所有底層細節都對用戶運行的程序進行隱藏。當程序想要使用硬件資源時,必須項操作系統發出請求
。內核對這個請求進行評估,如果允許使用這個資源,那么,內核代表應用程序與相關的硬件部分進行交互。
為了實現這種機制,現代操作系統依靠特殊的硬件特性來禁止用戶程序直接與底層硬件部分交互,或者禁止直接訪問任意的物理地址。特別是,硬件為CPU引入了至少兩種不同的執行模式:用戶程序的非特權模式和內核的特權模式。Unix把它們分別稱為用戶態
和內核態
。
多用戶系統就是一臺能并發和獨立地執行分別屬于兩個或多個用戶地若干應用程序地計算機
“并發”意味著幾個應用程序能同時處于活動狀態并競爭各種資源,如CPU,內存,硬盤等。
“獨立”意味著每個應用程序能執行自己地任務,而無需考慮其他用戶的應用程序在做什么。
當然從一個應用程序切換到另一個應用程序,會使每一個應用程序的速度有所減慢,從而影響用戶看到的響應時間。
深入理解php內核pdf?多用戶操作系統必須包含以下幾個特點
為了確保實現這些安全保護機制,操作系統必須利用與CPU特權模式相關的硬件保護機制,否則,用戶程序將能直接訪問系統電路并克服強加于它的這些限制。
在多用戶操作系統中,每個用戶在機器上都有私用空間;典型地,他擁有一定數量地磁盤空間來存儲文件、接收私人郵件信息等等。操作系統必須保證用戶空間地私有部分僅僅對其擁有者可見。
所有用戶由一個唯一的數字來標識
,這個數字叫用戶標識符(UID)。通常一個計算機系統只能由有限的人使用。當其中一個用戶開始一個工作會話時,操作系統會有一個認證機制。
為了和其他用戶有選擇地共享資料,每個用戶是一個或多個用戶組成員,組由唯一的用戶組標識符(user group ID)標識。
每個文件也恰好與一個組相對應。
任何地類Unix操作系統都有一個特殊用戶,root(超級用戶)。操作系統對root用戶不使用通常地保護機制,可以訪問任意一個文件,干涉每一個正在執行地用戶程序。
所有的操作系統都使用一種基本的抽象:進程。
一個進程可以定義為:“程序執行時的一個實例”,或者一個運行程序的執行上下文
。
在傳統的操作系統中,一個進程在地址空間中執行一個單獨的指令序列。地址空間是允許進程引用的內存地址集合。現代操作系統允許具有多個執行流的進程。也就是說,在相同的地址空間可執行多個指令序列
運行進程并發活動的系統稱為多道程序系統或多處理器系統
。區分程序和進程是非常重要的:幾個進程能并發的執行同一程序,而同一個進程能順序地執行幾個程序。
每個進程都自以為他是系統中唯一的進程,可以獨占操作系統所提供的服務。只要進程發出系統調用,硬件就會把特權模式由用戶態變為內核態,然后進程以非常有限的目的開始一個內核過程的執行。當內核過程完成,進程又退回到用戶態,然后進程從系統調用的下一條指令繼續執行。
單塊內核
:從整體上把內核作為一個大過程來實現,同時也運行在一個單獨的地址空間上。因此單內核通常以單個靜態二進制文件的形式存放于磁盤中。而進程管理、內存管理等是其中的一個個模塊,所有內核模塊都在這樣的一個大內核地址空間上運行。模塊之間可以直接調用相關的函數。效率高,緊湊性強。大多數的Unix系統都設計為單內核。Linux也是一個單內核,也就是說,Linux內核運行在單獨的內核地址空間上。
微內核
:
微內核并不作為一個單獨的大過程來實現,微內核的功能被劃分為多個獨立的進程程,進程程之間保持獨立并運行在各自的地址空間上。
微內核是一種功能更貼近硬件的核心軟件,它一般僅僅包括基本的內存管理、同步原語、進程間通信機制、IO操作和中斷管理(只是將OS中最核心的功能加入內核),這樣做有利于提高可擴展性和可移植性,但微內核與文件管理、設備驅動、虛擬內存管理、進程管理等其他上層模塊之間需要有較高的通信開銷。
所以為了達到微內核理論上的很多優點而又不影響性能,linux內核提供了模塊(module)。模塊是一個目標文件,其代碼可以運行時鏈接到內核或從內核解除鏈接。這種目標代碼通常由一組函數組成,用來實現文件系統、驅動程序或其他內核上層功能。與微內核操作系統外層不同,模塊不是作為一個特殊的進程執行。相反,與任何其他靜態鏈接的內核函數一樣,它代表當前進程在內核狀態下執行。
使用模塊化的主要優點:
模塊化方法:
因為任何模塊都可以在運行時被鏈接或解除鏈接,因此,系統程序員必須提出明確的軟件接口以訪問由模塊處理的數據結構。這使得開發新模塊變得容易。
平臺無關性:
即使模塊依賴于某些特殊的硬件特點,但它不依賴于某個固定的硬件平臺。
節省內存使用:
當需要模塊功能時,把它鏈接到正在運行的內核中,否則,將該模塊解除鏈接。這種機制對于小型嵌入式系統是非常有用的。
無性能損失:
模塊的目標代碼一旦被鏈接到內核,其作用與靜態鏈接的內核目標代碼完全等價。因此,當模塊的函數被調用時,無需顯式地進行消息傳遞。
Unix操作系統的設計集中反應在其文件系統上
Unix文件是以字節序列組成的信息載體,內核不解釋文件的內容。從用戶的角度看,文件被組織在一個樹結構的命名空間中。如下
除了葉節點之外,樹的所有節點都表示目錄。與樹的根相對應的目錄叫根目錄。
絕對路徑:
由/開頭,即從頂層的根目錄開始的路徑,就是絕對路徑
相對路徑:
由./或…/開頭,即從當前路徑或父目錄路徑開始的路徑,稱為相對路徑。
硬鏈接創建命令
ln p1 p2
即為由路徑p1標識的文件創建一個路徑為p2的硬鏈接。作用就是為了防止誤刪系統中的重要文件。其本質就是一個inode結點。和源文件的inode節點一模一樣的。刪除了源文件路徑,依舊可以通過該硬鏈接訪問文件,文件并不會刪除。
硬鏈接的限制:
只有在同一個文件系統中的文件之間才能創建硬鏈接
。現代Unix系統可能包含了多種文件系統,這些文件系統位于不同的磁盤或分區,用戶也許無法知道它們之間的物理劃分。為了克服硬鏈接的這些限制,就此誕生了軟鏈接
創建命令:
ln -s p1 p2
表示:創建一個p2新的軟鏈接,指向路徑名為p1.
軟鏈接就好比Windows下的快捷方式,通過軟鏈接可以導向文件真實存在的路徑,刪掉軟鏈接并不會真的刪除文件。(但是刪除源文件,軟鏈接就無效了,這就區別于硬鏈接)。
適用于任何的文件系統,且可以給目錄創建軟鏈接,會自動導向真實路徑。
Unix文件可以分為以下類型
前三種文件是Unix文件系統的基本類型
設備文件與I/O設備以及集成到內核中的設備驅動程序相關
。例如,當程序訪問設備文件時,它直接訪問與那個文件相關的I/O設備
管道和套接字是用于進程間通信的特殊文件
Unix對文件的內容和描述文件的信息給出了清楚的區分。除了設備文件和特殊文件系統文件外,每個文件都是由字符序列組成。
文件內容不包含任何控制信息,如文件長度或文件結束符(EOF)
文件系統處理文件需要的所有信息包含在一個名為索引節點(inode)的數據結構中。每個文件都有自己的索引節點,文件系統用索引節點來標識文件。
索引節點包含屬性:
文件的潛在用戶分為三種類型:
有三種類型的權限:讀、寫、可執行
通過wrx表示,所以一共有9種不同的二進制來標記。
還有三種附加的標記,suid,sgid,sticky用來定義文件的模式。當這些標記用到在可執行文件時有如下含義:
suid: 設置可執行文件的suid標志位,就獲得了該文件擁有者的UID
sgid:設置了可執行文件的sgid的標志位,就獲得了該文件用戶組的ID
sticky:設置了sticky標志位的可執行文件相當于向內核發送一個請求,當程序執行結束后,依然將他保留在內存中。(這個標志已經過時,目前使用基于代碼頁共享的其他方法)
當用戶訪問一個普通文件或目錄文件的內容時,他實際上是訪問存儲在硬件塊設備上的一些數據。從這個意義上來說,文件系統是硬盤分區物理組織的用戶視圖。因為處于用戶態的進程不能直接和底層硬件打交道,所以每個實際的文件操作必須在內核態下進行。因此,Unix操作系統定義了幾個與文件操作有關的系統調用
進程只能訪問“打開的”文件。問了打開一個文件,進程調用系統調用:
fd=open(path,flag,mode)
path:打開文件的路徑
flag:打開文件的方式(讀、寫、讀/寫,追加)
mode:指新創建的文件的訪問權限。
返回值是一個文件描述符(文件對象),注意打開文件的同步問題,如果需要上鎖,可以通過flock()函數實現上鎖。
為了創建一個新的文件,進程也可以調用create()系統調用,他與open()非常相似,都是由內核來處理。
對于普通Unix文件,可以順序訪問,也可以隨機訪問,而對設備文件和命名管道文件,通常只能順序地訪問。在這兩種訪問方式中,內核把文件指針存放在打開文件對象中,也就是說,當前位置就是下一次進行讀或寫地位置。
順序訪問是文件默認訪問方式,即read(),write()系統調用總是從文件指針地當前位置開始讀/寫。為了修改文件指針的值,必須在程序中顯式地調用
lseek()系統調用。
nowoffset=lseek(fd,offset,whence)
fd:文件描述符
offset:偏移量,有符號值
whence:表示文件指針新位置,有文件頭,當前位置,文件末尾三個選項。
read()系統調用需要以下參數
nread=read(fd,buf,count);
fd:文件描述符
buf:讀取出的數據存儲緩沖區
count:一次讀操作讀取的數據字節個數。
write和read相似,當讀到文件結束符EOF,read就會返回EOF表示已經讀到文件末尾(讀完了),同時在讀的過程中,文件指針會自動地加1。
當進程無需訪問文件時,就調用系統調用
res=close(fd)
fd:文件描述符
當一個進程終止時,內核會關閉其打開的所有仍然打開著的文件
系統調用更名:
res=rename(oldpath,newpath);
只是改了文件鏈接的名字。
刪除文件系統調用:
res=unlink(pathname)
;減少文件鏈接數,刪除了對應的目錄項。只有當鏈接項為0時,文件才被真正刪除。
Unix內核提供了應用程序可以運行的執行環境。因此,內核必須實現一組服務及相應的接口。應用程序使用這些接口,而且通常不會與硬件資源直接交互。
當一個程序在用戶態下執行時,他不能直接訪問內核數據結構或內核的程序。然而,當應用程序在內核態下運行時,這些限制不再有效。一個程序大部分時間都處于用戶態下,只有需要內核所提供的服務時才切換到內核態。當內核態滿足了程序的請求后,它讓程序又回到用戶態下。
進程是動態的實體,在系統內通常只有有限的生存期。創建、撤消及同步現有進程的任務都委托給內核的一組例程來完成。
內核本身并不是一個進程,而是進程的管理者。進程/內核模式假定:請求內核服務的進程使用所謂系統調用的特殊編程機制。每個系統調用都設置了一組識別進程進程請求的參數,然后執行與硬件相關的CPU指令完成從用戶態到內核態的轉換。
為了讓內核管理進程,每個進程由一個進程描述符表示,這個進程描述符包含有關進程當前狀態的信息。
當內核暫停一個進程執行時,就把幾個相關處理器寄存器的內容保存在進程描述符中。這些寄存器包括:
當內核決定恢復執行一個進程時,它用進程描述符中合適的字段來裝載CPU寄存器。因為程序計數器中所存的值指向下一條將要執行的指令,所以進程從它停止的地方恢復執行。
所有的Unix內核都是可重入的,這意味著若干進程可以同時在內核態下執行。
提供可重入的一種方式是編寫函數,以便這些函數只能修改局部變量,而不能改變全局數據結構。這樣的函數叫可重入函數。
如果一個硬件中斷發生,可重入內核能掛起當前正在執行的進程,即使這個進程處于內核態。
內核控制路徑表示內核處理系統調用,異常或中斷所執行的指令序列。
每個進程運行在它的私有地址空間。在用戶態下運行的進程涉及到私有棧,數據區和代碼區。當在內核態運行時,進程訪問內核的數據區和代碼區,但是使用另外的私有棧。
進程間也能共享一部分地址空間,以實現一種進程間通信,這就是System V引入并且已經被Linux支持的“共享內存”技術。
實現可重入內核需要利用同步機制:如果內核控制路徑對某個內核數據結構進行操作時被掛起,那么,其他的內核控制路徑就不應當再對該數據結構進行操作,除非它已經被重新設置成一致性狀態。否則,兩個控制路徑的交互作用將破壞所存儲的信息。
非搶占式內核就是指的進程在內核態執行時,它不能被掛起,也不能被另一個進程替代。只要進入了內核態,就一直到滿足需求退回到用戶態為止。但是這種內核設計在多處理器系統上運行是抵消的。
單處理器系統上的另一種同步機制是:在進入一個臨界區之前禁止所有硬件中斷,離開時再重新啟動中斷。這種機制盡管簡單,但是不是最佳。如果臨界區較大,那么在臨界區中停留的時間相對較長,這一段較長的時間內持續禁止中斷可能使所有的硬件活動處于凍結狀態。
此外,由于多處理器系統中禁止本地CPU上的中斷是不夠的,所以必須使用其他同步技術。
廣泛使用的一種同步技術就是是信號量。信號量僅僅是與一個數據結構相關的計數器
。所有內核線程在試圖訪問這個數據結構之前,都要檢查這個信號量。可以把每個信號量看成一個對象,其組成如下:
每個要保護的數據結構都有自己的信號量,其初始值為1
.
自旋鎖和信號量相似,但沒有進程鏈表;當一個進程發現鎖被另一個進程鎖著時,他就會不停的“旋轉”,執行一個緊湊的循環指令直到鎖打開。
死鎖:進程P1獲得了訪問數據a的權限,但是在等待進程P2釋放b的訪問權限,進程P2拿到了b的訪問權限,但是在等待P1釋放a的訪問權限。這樣一個互等的狀態就是死鎖。
Unix信號(signal)提供了把系統事件報告給進程的一種機制。每種事件都有自己的信號編號,通常用一個符號常量來表示。
進程可以用兩種方式對接收到的信號做出反應:
SIGKILL和SIGSTOP信號不能直接由進程處理,也不能直接忽略
AT&T的Unix System V引入了在用戶態下其他種類的進程間通信機制,很多Unix內核也采用了這些機制:信號量、消息隊列以及共享內存。它們被統稱為System V IPC。
Unix在進程和它正在執行的程序之間做出了清晰的劃分。fork()和_exit()系統調用發別來表示創建一個新進程和終止一個進程,而調用exec()類系統調用則是裝入一個新程序。
exit() 和_exit()函數的區別就是_exit()不會刷新流,exit是一個C庫函數,_exit()是一個系統調用
調用fork()進程的是父進程,而新進程是它的子進程。父子進程能相互找到對方,因為描述每個進程的數據結構包含兩個指針,一個指向父親,一個指向子進程。
實現fork()一種天真的方式就是將父進程的數據與代碼都復制,并把這個拷貝賦予子進程
_exit()系統調用終止一個進程。內核對這個系統調用的處理就是通過釋放進程所擁有的資源并向父進程發送SIGCHILD信號來實現
父進程通過wait()系統調用函數來查詢子進程是否已經終止了。wait()系統掉調用允許進程等待,直到其中的一個子進程結束;它返回已終止子進程的進程標識符(PID)。
孤兒進程:就是父進程已經結束了,但是子進程還沒結束,則子進程就沒了父親。出現這種情況一般該子進程會被init進程收養,但是有的設置的是被最近的祖宗收養。
僵死進程:子進程結束了,但是父進程沒有接收到子進程結束的信號,導致該子進程的資源無法被回收。
每個進程描述符包括一個包含進程組ID的字段。每個進程組可以有一個領頭進程(即其PID與這個進程組ID相同的進程)。新創建的進程最初被插入到其父進程的進程組中。
所有新近的Unix系統都提供了一種有用的抽象,叫虛擬內存。虛擬內存作為一種邏輯層,處于應用程序的內存請求與硬件內存管理單元(MMU)之間。虛擬內存有很多用途和優點:
虛擬內存子系統的主要成分是虛擬地址空間的概念。進程所用的一組內存地址不同于物理內存地址。當一個進程使用虛擬地址時,內核和MMU協同定位其在內存中的實際物理位置。
所有的Unix操作系統都將RAM毫無意義地劃分為兩部分,其中若干兆字節專門用于存放內核映象。RAM的其余部分通常由虛擬內存系統來處理,并且用在以下三種可能的方面:
內存內核分配器是一個子系統,它試圖滿足系統中所有部分對內存的請求。
其中一些請求來自內核其他子系統,它們需要一些內核使用的內存。還有一些來自用戶程序的系統調用,用來增加用戶進程的地址空間。一個好的KMA應該具有如下特點:
幾種KMA算法:
內核通常用一組內存區描述符描述進程虛擬地址空間。內核分配給進程的虛擬地址空間由以下內存區組成:
物理內存的一大優勢就是用作磁盤和其他塊設備的高速緩存。因為硬盤非常的慢,與RAM的訪問時間相比,太長了。所以設置了一個高速緩存Cache。用于提高對磁盤中的數據訪問。
通過sync()系統調用把所有"臟"的緩沖區(即緩沖區的內容與對應磁盤塊的內容不一樣)寫入磁盤中來強制磁盤同步。(周期性的)
內核通過設備驅動程序與I/O設備交互。設備驅動程序包含在內核中,由控制一個或多個設備的數據結構和函數組成,這些設備包括硬盤,鍵盤,鼠標,監視器,網絡接口以及連接到SCSI總線上的設備。通過特點的接口,每個驅動程序與內核中的其余部分(甚至與其他驅動程序)相互作用這種方式的優點有如下:
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态