本文主要是《Linux內核設計與實現》這本書的讀書筆記,這本書我讀了不下十遍,但依然感受囫圇吞棗。我結合本身的理解,從這本書中整理出了一些運維應該瞭解的內核知識,但願對你們可以有所幫助。另外,推薦你們讀下這邊書,這本書主要講內核設計、實現原理和方法,有利於理解內核的一些機理。linux
目錄程序員
運維爲何要了解內核算法
進程編程
系統調用緩存
中斷安全
內核同步服務器
定時器和時間管理網絡
內存分配數據結構
虛擬文件系統併發
塊I/O層
I/O算法
頁高速緩存和頁回寫
關於內核的幾個概念
1、運維爲何要了解內核
運維爲何要了解內核
大神Linus說了解內核的方法就是閱讀源碼(*Read The Fucking Source Code*),可是linux內核學習曲線公認的陡峭,對於運維來講難度很是大,並且現代Linux已經很是龐大,別說運維了,就是專門從事Linux內核開發的人,也不可能瞭解到內核的所有代碼。
可是運維應該瞭解內核的工做原理,設計哲學,瞭解CPU、網絡的調度方法,瞭解內存、文件系統的結構。
瞭解了Linux系統如何工做,咱們才能更好的使用它,讓它爲咱們服務。
Linux的由來
內核爲何吸引人,很重要的一個緣由是自由精神,能夠隨手拿到源碼,只有願意,能夠了解到每一個功能很是細微的地方。
Linux內核是如何來的,1991年,芬蘭的大學生Linus熱衷於使用Minix,一種教學用的Unix系統,可是他不能隨意修改和發佈該系統的源代碼,這令他對這個系統的設計理念感到失望,因而就本身在386上設計了一款系統,併發布到了互聯網上,很快就流行了起來。
順便說下,Linux的吉祥物爲何是企鵝,那是由於Linus小的時候,被一隻企鵝咬過,令他印象深入。關於Linus還有一本書,叫作《只是爲了好玩--linux之父林納斯自傳》,你們有興趣能夠閱讀下。
我這裏有一些數據,來自2017年度Linux內核開發者報告,經過這些數字,你們對目前的內核生態會有簡單的瞭解。
目前,已有超過1400家公司的15600名開發人員參與了Linux內核的開發。僅就2016年到2017年,超過500家公司的4300多名開發人員對內核作出了貢獻;其中有1670個開發者是第一次貢獻,約佔貢獻者的三分之一。
2017年度,贊助Linux內核開發的十大組織包括英特爾、Red Hat、Linaro、IBM、三星、SUSE、谷歌、AMD、Renesas和Mellanox。
Linux開發的速度繼續增長,參與開發的人員和公司的數量也在不斷增長。內核每小時的平均變化量爲8.5,比2016年報告中的7.8個變化顯著增長,這意味着天天有204個變化,每週超過1400個變化。
從2016年的66天開始,平均每一個版本的開發天數從去年的66天增長到67.66天,每個版本的間隔時間分別爲63或70天,提供了顯著的可預測性。4.9和4.12開發週期的特色是,在內核項目歷史上看到的最高補丁率。
未領取薪酬的開發者可能正在趨於穩定,這些開發者貢獻了8.2%的貢獻,比去年的7.7%有所增長。這一數字仍遠低於2014年的11.8%。這多是因爲內核開發人員短缺,致使那些有能力提交必定質量補丁的人,在找到工做時沒有困難。
新加入內核開發的前三名是英特爾、谷歌、華爲,其中華爲投入33名工程師。
Linux內核的設計哲學
Linux內核設計參考了Unix,而且兼容Unix API,可是Linux內核吸取了Unix系統的優勢,摒棄了一些缺點。
先來了解一個概念,單內核和微內核。
單內核是總體單獨的一個過程,存儲方式每每也是一個大的二進制文件,使用的也是連續的一整塊內存。全部服務都運行在內核態,內核之間的通訊就很容易,內核能夠直接調用函數。
微內核是按照功能劃分爲多個獨立過程,這個過程叫作服務器,只有少數特權服務的服務器才運行在特權模式下,大部分服務運行在用戶空間。大部分服務都使用本身的內存地址,不可能像單內核那樣直接調用函數,而是要經過消息傳遞,系統採用進程間通信的機制,專業術語叫IPC機制。這樣的好處是一項服務失效,並不會影響到其餘服務,由於彼此隔離。
由於IPC機制的開銷多用於函數調用,有大量的內核空間和用戶空間的上下文切換,所以,消息傳遞須要必定的週期,而單內核就沒有這個問題。
這樣還形成一個結果,就是實際上,微內核爲了提升效率,會讓大部分服務位於內核態。
Windows NT內核系統,包括Windows7 Windows10 Windows Server系列,MacOS都是典型的微內核系統。
前段時間,華爲推出的鴻蒙系統,也宣稱是微內核系統。
Linux系統是單內核系統,也就是說Linux系統運行在單獨的內核地址空間上,不過Linux吸收了微內核的精華,引入了模塊化設計,搶佔式內核,支持內核線程,及動態裝載內核的能力。同時還避免了微內核設計上的性能損失。
可見Linux的設計哲學是實用主義優先。
再解釋下什麼是內核搶佔,搶佔指的是內核具備容許在內核運行的任務優先執行的能力,大部分Unix系統是不支持這個能力的。
再來介紹下內核的版本,內核有兩種版本,穩定版和開發版,穩定版有工業級的強度,能夠普遍部署,開發版主要用於實現新的功能。
Linux內核經過簡單的命名機制區分穩定版和開發版,使用3個或者4個點分隔數字,表明不一樣的版本,第一個數字是主版本號,第二個數字是從版本號,第三個數字是修改版本號,第四個數字是可選,是穩定版本號。從第二個數字能夠看出是穩定版仍是開發版,若是是偶數就是穩定版,若是是奇數就是開發版。
好比內核版本2.6.26.1就是穩定版,由於它的第二個數字是6,是偶數。內核版本4.9就是開發版,由於9是奇數。
2、進程
先來聊聊Linux內核開發,內核開發和普通應用開發有兩個地方不同:
本身要管理內存,普通應用跑在內核之上,內核能夠幫你管理內存,可是你本身就是內核,你必須本身作好內核管理,要不很容易就內核溢出了。
沒有庫文件,普通應用程序有不少庫文件能夠調用,內核開發則沒有,內核開發就是標準的C。
由此看見,作內核開發仍是要對內核有深入的理解才能夠,請注意,這裏的內核開發指的是內核核心功能的開發。
咱們再來看看進程,進程簡單的講,就是運行中的程序,我我的理解,進程是一種生命形式,就像一我的的生命,從呱呱墜地開始一直到生命的終結,中間須要不停的從周圍的環境吸取資源,而且對環境也施加影響。
進程須要的資源就是CPU、內存、文件、網絡等資源,進程雖然是從程序文件開始,可是不等於程序文件,一個程序文件能夠啓動多個進程,一個進程也多是由多個程序文件產生的,因此進程是一種運行中的狀態。
內核用一個雙向循環鏈表來描述進程的狀態,這一鏈表在32位的機器上是1.7KB大小,鏈表中的每一項都是類型爲task_struct,稱爲進程描述符的結構。進程描述符就不詳細介紹了。
下面咱們來看看進程的狀態標誌,進程有5種狀態標誌:
第一 task_running 運行,進程正在運行,或者正在隊列中等待運行,運行的進程能夠在用戶空間,也能夠在內核中。
第二 task_interruptible 可中斷,或者被阻塞,等待某些條件,一旦達到條件就被喚醒,而後進入運行狀態。
第三 task_uninterruptible 不可中斷,這種狀態,即便收到外部的信號,也不會被喚醒,這種狀態通常用的比較少。
第四 task_traced 被其餘進程跟蹤,例如經過ptrace對進程進行跟蹤調試。
第五 task_stoped 中止,進程沒有運行也不能運行的狀態。
下面在介紹幾個概念
第一個概念,進程上下文
進程從可執行文件載入進程的內存地址空間運行,通常是在用戶空間,當進程調用了系統接口,或者觸發了某種異常,它就進入了內核空間,此時,咱們稱內核表明進程執行,並處於進程上下文中,總結下,就是內核和進程交互的時候,就是上下文狀態,請注意,後面咱們還會介紹中斷的上下文,和進程的上下文是有區別的,中斷上下文中,系統不表明進程執行,而是執行一箇中斷程序。
第二個概念,進程家族樹
Linux系統中,全部的進程都是PID爲1的init進程的後代,內核在系統啓動的最後階段啓動init進程,該進程讀取系統的初始化腳本,並執行其餘的相關程序,最終完成系統的啓動。
系統中的每一個進程必有一個父進程,每一個進程也會擁有靈感或者多個子進程,擁有同一父進程的全部進程被稱爲兄弟進程,進程間的關係也保存在前面提到的進程描述符中。
第三個概念,寫時拷貝
Linux系統建立新的進程的時候,使用的是寫時拷貝的技術,這樣的好處是能夠推遲甚至免除數據拷貝,子進程共享父進程的資源,只有當須要寫入的時候
咱們經過幾個概念的解釋,來清晰化下內核對進程的調度
第一 什麼是進程調度
進程調度就是決定進程何時運行,能夠運行多長時間,進程調度程序的使命就是儘量的讓進程多運行,提升效率。
第二 什麼是多任務
多任務就是可以併發的執行多個進程,在單處理器上,這是一個假象,其實就是多個進程快速的在處理器上快速切進切出。
第三 什麼是搶佔式內核
多任務系統能夠劃分兩類,非搶佔式多任務和搶佔式多任務,搶佔式多任務就是由內核決定何時中止進程的運行,這個強制的動做就叫搶佔。相反,除非進程主動中止,不然就一直運行,就是非搶佔式多任務,顯然,非搶佔式多任務要依靠進程的自覺和良好設計,很古老的Windows3.1就是這樣的系統,我大概是20年前接觸到的,1996年的時候,這樣的系統一個特色就是容易死機,可是當時看慣了黑黑屏幕的dos,看到窗口式的Windows,給人仍是很是震撼的感受。
第四 時間片
進程被搶佔以前的時間是預先設置好的,有一個專門的名字,就是進程的時間片,調度策略必須規定一個默認的時間片,這裏須要平衡,時間片太長影響系統的交互體驗,時間片過短,會增長進程切換的頻率,引發過多處理器消耗。
許多操做系統有默認的時間片長度,好比10ms,可是linux沒有默認的時間片長度,Linux按照比例來劃分,這樣負載大的進程得到的處理器使用時間就更長。
第五 Linux的調度算法
在2.4內核之前,Linux內核調度很簡陋,2.5內核中引入了Q(1)的調度程序,能夠完美支持幾十個處理器的進程調度,可是Q(1)算法對對時間敏感的程序有一些先天不足,所以Q(1)適合服務器,可是不適合桌面系統。
2.6內核中,引入了徹底公平算法,簡稱是CFS,目前Linux系統默認使用的都是CFS算法。
第六 IO消耗型和處理器消耗型的進程
IO消耗型的進程總在等待IO請求,佔有處理器時間比較少,大部分用戶圖形界面程序都是IO消耗型。相反,若是處理器消耗型進程,就是把時間大多用於代碼執行上,IO請求比較少。
固然也有便是IO消耗型也是處理器消耗型的進程,好比字處理程序,大部分時間是IO消耗型,可是當執行拼寫檢查的時候,就是處理器消耗型。
進程調度策略常常要在進程響應速度和最大系統利用率之間找平衡,這個背後是複雜的算法,不一樣操做系統的傾向性也不同,Linux系統傾向io消耗型,這樣響應速度快,用戶體驗好。
第七 進程優先級
Linux採用兩種不一樣的優先級範圍:
第一種是nice值,範圍是-20到+19,默認是0,越低的nice值,能夠得到更多的處理器時間。
第二種是實時優先級,變化範圍是0到99,和nice值相反,越高的值,優先級越高。
兩種優先級劃分有什麼區別,任何實時進程優先級高於普通進程,就是說兩種優先級處於互不交互的兩個範疇。
進一步說明下, 進程分爲普通進程和實時進程,普通進程使用CFS算法調度,優先級按照nice值區分。
實時進程有兩種調度算法,FIFO,即先進先出,這種進程一直佔用處理器,直到本身受阻塞或者釋放處理器,若是有多個FIFO優先級進程,則會輪流執行。
另一種實時進程調度算法是RR,RR進程是按照時間片分配的,優先級範圍就是0到99 。
注意,再強調下,實時進程總會搶佔普通進程。
3、系統調用
用戶空間進程不是和硬件設備直接通信的,而是有一箇中間層,這樣作的好處有三個:
第一, 爲用戶空間提供了一種硬件的抽象接口,這樣用戶空間進程就不用關心具體的硬件信息。
第二,限制了用戶空間進程的行爲,防止對其餘進程形成影響,保證了系統的穩定和安全。
第三,隔離進程使用的資源, 方便內核調度。
通常的進程調用是經過API實現的,不是直接調用內核,API有一套標準,叫POSIX,Unix,Linux,甚至Windows都支持POSIX,只是你們支持的程度不同。
具體內核的API如何實現,這個要依靠Linux內核程序員,關於系統調用,運維瞭解到這些知識就能夠了。
4、中斷
仍是經過幾個概念來了解中斷。
第一 什麼是中斷
中斷就是鍵盤、鼠標、硬盤、顯卡、網卡等硬件和處理器的通信。
大部分硬件的運行速度和處理器比起來低不少,硬件要和處理器通信,有兩種方式,一種方式是處理器輪詢各個硬件,一種方式是硬件主動來找處理器,其實是硬件給處理器主動上報,由於這種方式效率更高,硬件在須要的時候給處理器發出信息,處理器來響應,這個就是中斷處理。
中斷信息實際就是電信號,硬件,好比鍵盤控制器,在你敲擊鍵盤的時候會發出中斷,信號進入中斷控制器,而後進入處理器,處理器再通知操做系統。
第二 IRQ
不一樣的設備對應的中斷不一樣,每一箇中斷都有惟一的數字標誌,這樣系統就能區分具體的設備,這些中斷值被稱做IRQ,中文的意思就是中斷請求線。
好比,在經典的PC機上,IRQ0是時鐘中端,IRQ1是鍵盤中斷,可是這樣也有問題,設備愈來愈多,原來的設計,中斷號有限,常常會引發衝突,我記00年初,剛有聲卡的時候,常常聲卡由於中斷衝突而不能使用,解決方法就是更換一個PCI插槽。
因此後來就有了動態分配中斷值的方法,PCI設備都是動態分配中斷號的,最終的目標關鍵是硬件能和處理器通信,可以引發處理器注意。
第三 異常
異常簡單的說,就是程序出錯,須要內核來處理的時候,一般因爲編程失誤而致使的錯誤指令,好比被0除,或者是在執行期間出現特殊狀況,好比缺頁。這時候就須要內核來處理,由於處理器體系結構處理異常與處理中斷方式相似,所以,內核對他們的處理也很相似,實際上,異常也經常被稱爲同步中斷。
第四 中斷處理程序
在響應一個特定的中斷的時候,內核會執行一個函數,這個函數就是中斷處理程序interrput handler ,或者中斷服務例程interrupt service routine,簡稱ISR。產生中斷的每一個設備都一個相應的中斷處理程序。
第五 中斷的上半部和下半部
又想中斷處理程序運行的快,又想中斷處理程序完成的工做量多,這是矛盾的,爲了解決這個矛盾,咱們把中斷處理切爲兩個部分,中斷處理程序是上半部top half,接收到中斷,當即開始執行,但只作嚴格時限的工做,例如對接收的中斷進行應答和復位硬件,這些工做都是在全部中斷被禁止的狀況下完成的,因此必須儘量快的完成。可以被容許稍後完成的工做推遲到下半部,bottom half.
用網卡作一個例子解釋下,當網卡接收到網絡的數據包的時候,須要通知內核數據包到了,網卡須要當即完成這件事,從而優化網絡的吞吐量和傳輸週期,以免超時。這時候中斷開始執行,通知硬件,拷貝最新的網絡數據包到內存,而後讀取網卡更多的數據包,這些都是重要、緊張而又與硬件相關的工做。
內核須要快速拷貝網絡數據包到內存,由於網卡的緩存的大小是固定的,若是速度不夠快,就會形成溢出,網卡就會丟棄數據包。
當數據拷貝到內存,中斷的任務就完成了,它將控制權交還給系統系統中斷前運行的程序,數據處理在隨後的下半部進行。
第六 中斷上下文
當執行一箇中斷處理程序的時候,內核處於中斷上下問interrput context,咱們回憶下前面提到的進程上下文,進程上下文是內核所處的操做模式,此時內核表明進程執行。
與進程上下文相反,中斷上下文和進程沒有關係,由於沒有後備進程,因此中斷上下文不能夠睡眠,中斷上下文有嚴格的時間限制,由於它打短了其餘代碼,
在Linux系統中,查看中斷的狀況,可使用命令,能夠看出詳細的中斷狀況:
cat /proc/interrputs
中斷上半部處理須要緊急處理的任務,包括對時間敏感,和硬件息息相關,不但願被其餘中斷打斷的任務,其餘不緊急的任務,都交給下半部處理。
一般咱們但願儘量的將任務交給中斷下半部處理,由於上半部處理的時候,會形成其餘中斷被屏蔽,那麼下半部是如何處理的呢,有三種方法。
第一種方法, BH,即bottom half,這是最先的中斷處理機制,也是早期的惟一方法,同時只能有一個BH處理,即便有多個處理器。從內核2.5 版本開始,BH方法已經被放棄。
第二種方法,任務隊列,爲了充分使用多處理性能,內核開發者引入了任務隊列的機制,task queue,內核定義了一組隊列,驅動程序來和隊列匹配,任務隊列的方案在處理性能要求比較高的子系統,好比網絡部分,也不能勝任。
第三種方法,軟中斷和tasklet,這種方法是在內核2.3版本中引入的,軟中斷能夠在全部處理器上同時執行,tasklet是一種基於軟中斷實現的靈活性強、動態建立的下半部實現機制,兩個不一樣類型的tasklet能夠同時在不一樣的處理器上執行,可是類型相同的tasklet不能同時執行,tasklet是性能和易用性之間平衡的產物,能夠處理大部分下半部中斷處理。像網絡這樣對性能要求比較高的狀況,才須要使用軟中斷。
5、內核同步
咱們仍是經過幾個概念來了解下什麼是內核同步。
第一個概念 爲何會有內核同步問題
在使用共享內存的應用程序中,程序員必須特別留意保護共享資源,防止共享資源併發訪問,防止多個線程同時訪問和操做數據,形成數據互相覆蓋,和數據不一致。
在單一處理器的時候,這個還好辦,只有在中斷髮生,或者從新調度另外一個任務的時候,數據纔可能被併發訪問。
到了多處理的時代,問題變的複雜,多處理器意味者着內核代碼能夠同時在兩個或者兩個以上的處理器上運行,爲了防止同時改寫內存數據的狀況發生,就必須引入內核同步機制。
第二個概念 臨界區和競爭條件
臨界區是指訪問和操做共享數據的代碼段,多個執行線程併發訪問同一個資源一般是不安全的,爲了不在臨界區中併發訪問,編程者必須保證這些代碼是原子的執行,也就是說,操做在執行結束前不可被打斷,就如同整個臨界區是一個不可分割的指令同樣。若是兩個執行線程有可能處於同一個臨界區中同時執行,那麼就是程序包含的bug。若是這種狀況確實發生了,咱們就稱它爲競爭條件,這種狀況出現的機會很是小,就是由於競爭引發的錯誤很是不容易重現,因此調試這種錯誤纔會很是困難,避免併發和防止競爭條件稱爲同步。
第三個概念,加鎖
爲了防止一個處理器的進程在處理數據,而另一個處理器上的進程也同時修改這些數據,就須要給這塊數據加鎖,確保同時只能有一個進程訪問數據。
加鎖也是技術活,鎖有多種多樣的形式,加鎖的粒度和範圍也各不相同。
第四個概念 僞併發和真併發
在單處理器上,用戶進程可能在任什麼時候刻被搶佔,也可能形成共享內存被修改,兩個進程是交叉進行的,因此被稱爲僞併發。
在多處理器上,有可能真的兩個進程在同時訪問共享內存,所以被稱爲真併發。
內核中有如下相似的可能,形成併發執行,他們是:
中斷,中斷可能隨時打斷正在執行的代碼。
軟中斷和tasklet,內核能在任什麼時候刻喚醒或者調度軟中斷和tasklet,打斷當前正在執行的代碼。
內核搶佔,由於內核具備搶佔性,內核中的任務可能會被另外一任務搶佔。
睡眠及用戶空間的同步,在內核執行的進程可能睡眠,這就會喚醒調度程序執行另一個進程。
對稱多處理,兩個或者多個處理器同時執行代碼。
第五個概念,死鎖
死鎖的產生須要必定條件,要有一個或多個執行線程和一個或者多個資源,每一個線程都在等待其中的一個資源,但全部的資源都被佔用了。全部線程都在等待,但他們永遠不會釋放已經佔有的資源,因而全部線程都沒法繼續,這便意味着死鎖的發生。如何防止死鎖的發生,也是程序設計的時候要考慮的問題。
第六個概念 爭用和擴展性
一個資源被鎖定,多個進程都在競爭這個資源,被稱爲鎖的爭用,鎖的爭用會形成系統瓶頸,嚴重下降系統性能。
解決辦法就是擴展性,將鎖的範圍儘可能精細,這樣就能夠減小鎖的爭用,可是過於精細,也會額外消耗系統資源,因此掌握好平衡就須要技巧。
6、定時器和時間管理
時間管理在內核中佔有很是重要的位置,內核中的函數驅動方式,能夠分爲事件驅動和時間驅動,其實時間驅動也能夠認爲是特殊的事件驅動,可是內核中,時間驅動的頻率特別高。
時間驅動也能夠分爲週期驅動,好比每秒100次,或者推後執行,好比500ms之後執行某個任務。
另外,內核還必須管理系統的運行時間以及當前日期和時間。
這裏還有一個概念,相對時間和絕對時間,若是某個事件在5s以後被執行,那麼系統須要的是相對時間,相反,若是要求管理當前日期和當前時間,則內核不但要計算流逝的時間並且還要計算絕對時間。
週期性產生的事件,好比每10ms一次,都是由系統定時器產生的,系統定時器是一種硬件可編程芯片,能夠固定頻率產生中斷,這個中斷就是定時器中斷.
在x86體系中,系統定時器默認頻率是100Hz,也就是說i386處理器上每秒中斷100次,即10ms一次,注意,每種體系的頻率可能不同,有的是250,有的是1000。頻率能夠在編譯內核時指定。
從2.5內核版本開始,中斷頻率被設定爲1000Hz,使用高頻率的好處是準確度,精確性更高,可是同時系統負擔更重,也更耗電,可是處理器性能愈來愈高,這點消耗不會對系統形成過大的影響。
7、內存分配
內核把物理頁做爲內存管理的基本單元,儘管處理器的最小可尋址單元一般爲字(甚至字節),可是,內存管理單元MMU一般以頁爲單位進行處理。
體系不一樣,頁的大小也不同,大部分32位體系結構支持4KB的頁,64位體系結構通常支持8KB的頁,這意味着,在1GB物理內存的機器上,4KB頁大小,物理內存會被劃分爲262144個頁。
因爲硬件的限制,內核並不能對全部的頁一視同仁,有些頁位於特定的物理地址上,因此不能將其用於特定的任務,因爲存在這種限制,因此內核把頁劃分爲不一樣的區zone。
Linux必須處理以下兩種因爲硬件存在缺陷引發的尋址問題:
一些硬件只能用某些特定的內存地址來執行DMA,即直接內存訪問。
一些體系結構的內存物理尋址範圍比虛擬尋址範圍大得多,這樣,就有一些內存不能永久地映射到內核空間上。
由於存在這些限制條件,Linux主要使用了四種區:
ZONE_DMA 這個區包含的頁用來執行DMA操做
ZONE_DMA32 和ZONE_DMA類似,可是這個區只能被32位設備訪問
ZONE_NORMAL 這個區包含的都是能正常映射的頁
ZONE_HIGHEM 這個區包含高端內存,其中的頁不能永久映射到內核地址空間。
通常DMA區使用0-16MB的內存,NORMAL區使用16-896MB的內存,HIGHEM區使用896MB以上的內存。
8、虛擬文件系統
虛擬文件系統做爲內核的子系統,簡稱VFS,爲用戶空間程序提供了文件和文件系統相關的接口,系統中的全部文件系統不但依賴VFS共存,而且依靠VFS協同工做。
VFS提供了通用的接口和方法,好比open(),read(),write(),系統調用的無需考慮具體文件系統和實際物理介質。
之因此能夠這樣,是由於內核在底層文件系統接口上創建一個抽象層,抽象層使Linux可以支持各類文件系統。VFS抽象層定義了全部文件系統都支持的、基本的、概念上的接口和數據結構。任何新的文件系統和新介質只要符合VFS規範,均可以直接使用。
unix系統使用四種和文件系統相關的傳統抽象概念:文件、目錄項、索引節點和掛載點。從本質上講文件系統是特殊的數據分層存儲結構,包含文件、目錄和相關的控制信息。
VFS採用面向對象的設計思路,使用一組數據結構來表明通用文件對象。
VFS有四個主要的對象類型:
超級塊對象,表明一個具體的已安裝文件系統;
索引節點對象,表明一個具體文件;
目錄項對象,表明一個目錄項,是路徑的一個組成部分;
文件對象,表明由進程打開的文件。
另外,說明下,由於VFS將目錄做爲文件來處理,因此不存在目錄對象。
咱們總結下,Linux支持了多種類型的文件系統,從本地文件系統,例如ext3,ext4,到網絡文件系統好比NFS。LInux在標準內核中已支持的文件系統超過60種。VFS層提供給這些不一樣文件系統一個統一的實現框架,並且也提供了能和標準系統調用交互工做的統一接口。因爲VFS層的存在,使得Linux上實現新文件系統的工做變得簡單起來,它能夠輕鬆地使這些文件系統經過標準Unix系統調用而協同工做。
9、塊I/O層
咱們仍是經過五個概念瞭解塊IO層
第一個概念 塊設備和字符設備。
系統可以隨機訪問固定大小數據片的硬件設備稱爲塊設備,數據片的英文術語是chunk,硬盤、軟盤、光盤、SSD、U盤都屬於塊設備,由於系統隨時能夠訪問這些介質上的任意位置數據,另外,說明下,對這些介質的訪問,是經過訪問文件系統實現的。
字符設備是按照字符流的方式被有序訪問的設備,像串口和鍵盤就屬於字符設備。
塊設備和字符設備主要的區別就是隨機訪問方式仍是順序訪問方式。
第二個概念 扇區和塊。
塊設備中最小的可尋址單元是扇區,扇區大小通常是2的整數倍,硬盤最多見的扇區大小是512字節,CD-ROM的扇區通常是2KB。
每種文件系統都有本身最小的邏輯可尋址單元,塊。塊是文件系統的抽象,只能基於塊來訪問文件系統。
扇區和塊的區別是,物理磁盤尋址是按照扇區級進行的,文件系統是按照塊來進行的。塊大小必須是扇區的倍數,通常是2的整數倍,而且不能超過一個內存頁大小,由於文件塊須要被緩存到內存中。因此通常文件塊的大小是512字節,1KB,4KB。
另外,磁盤還有一些術語,好比簇,柱面,磁頭,請你們本身找資料看下。
第三個概念 緩衝區。
當一個塊被調入內存時,就存儲在一個緩衝區中,每一個緩衝區與一個塊對應,至關於磁盤塊在內存中的表示。像前面介紹的,塊包含一個或多個扇區,可是大小不能超過一個頁面,因此一個內存頁能夠容納一個或者多個內存中的塊。
第四個概念 請求隊列。
塊設備將它們掛起的塊IO請求保存在請求隊列中,請求隊列只要不爲空,隊列對應的塊設備驅動程序就會從隊列頭部獲取請求,而後將其送入對應的塊設備上去。
第五個概念 IO調度程序。
若是簡單的之內核產生請求的次序直接將請求發向塊設備的話,性能確定讓人難以忍受,磁盤尋址是整個計算機中最慢的操做之一,每次尋址,定位磁頭到特定的塊上的某個位置,須要花費很多時間,因此儘可能縮短尋址時間無疑是提升系統性能的關鍵。
爲了優化尋址操做,內核既不會簡單的按請求接收文件,也不會馬上將其提交給磁盤。相反,內核會在提交前,先執行合併與排序的操做,這種操做能夠極大的提升系統性能,在內核中負責提交IO請求的子系統,稱爲IO調度程序。
IO調度程序將磁盤IO資源分配給系統中全部掛起的塊IO請求,這種資源分配是經過請求隊列中掛起的請求合併和排序來完成的。
IO調度器的工做是管理塊設備的請求隊列,它決定隊列中的請求排列順序以及在什麼時刻發送請求到塊設備,這樣作有利於減小磁盤尋址時間,從而提升全局吞吐量。注意,全局這個定語很重要,由於IO調度器可能爲了提升系統總體性能,會對某些請求不公。
IO調度器經過兩種方法減小磁盤尋址時間,合併與排序。舉個例子,文件系統接到多個請求隊列,IO調度器能夠按照磁盤扇區順序進行排序,那麼相鄰扇區的訪問就能夠合併爲一次,這樣就大大減小了磁盤尋址消耗。即便沒有相鄰扇區的訪問,經過IO調度器,按照磁盤旋轉方向訪問,也縮短了全部請求的磁盤尋址時間。
10、I/O算法
第一種算法 linus電梯
在2.4版本內核中,linus是默認的IO調度程序,linus算法可以執行合併與排序預處理,當有新的請求加入隊列時,它首先檢查其餘每個掛起的請求是否能夠和新請求合併。linus電梯算法能夠執行向前和向後合併,若是新的請求沒有合適的插入點,則會被放入隊列尾部。
另外,系統中若是有駐留時間過長的請求,新的請求也會被放到隊列尾部,這樣作的目的是防止對一個磁盤位置訪問的過多,形成對其餘磁盤位置的請求被餓死。可是這樣的作法,由於僅僅是改變隊列排序,沒有隊列的時間檢測,不能徹底避免有隊列被餓死的狀況。
第二種算法 最終期限
最終期限deadline IO調度算法是爲了解決linus電梯算法所帶來的飢餓問題而提出的。出於減小磁盤尋址時間的考慮,對某個磁盤區域的頻繁操做,會使對磁盤其餘位置的操做請求餓死。
更糟糕的是,普通的請求還會形成寫-飢餓-讀這種問題。
寫請求一般能夠緩存,可是讀請求的時候,程序會被阻塞,直到拿到請求的讀數據,也就是寫請求是異步的,讀請求是同步的,若是有大量的讀請求的時候,寫請求就會被餓死。
問題可能還會更嚴重,若是讀請求和寫請求是相互依靠的,寫請求沒有操做,讀操做又去請求數據,就會形成應用更長時間的等待。
最終期限算法中,每一個請求都有一個超時時間,默認讀請求的超時時間是500ms,寫請求的超時時間是5s。
最終期限算法有三個隊列,在超時時間內,調度相似於linus電梯,有一個排序隊列,另外維護兩個按照時間順序的讀fifo隊列,和寫fifo隊列。
在超時時間內,按照排序隊列派發操做,若是讀寫隊列的列頭請求超時,那麼IO調度程序便從隊列中提取請求進行服務,這樣就能保證不發生磁盤操做請求超時的狀況。
經過最終期限算法,能夠避免寫操做餓死,同時由於讀操做超時時間短,這種算法也優化了大量讀操做的響應。
第三種 算法 預測IO調度
預測IO調度和最終期限同樣,也是維護三個同樣的隊列,不一樣的是,在提交請求的以前,會有意等待一段時間,默認是6ms,若是有新的請求來,在將相鄰扇區的請求合併,這樣能夠優化磁盤操做。固然,若是沒有操做請求,會浪費幾毫秒的時間。
第四種算法 徹底公平的排隊IO調度CFQ
CFQ調度程序把進入IO的請求放去特定的隊列中,這種隊列請求是根據引發IO請求的進程組織的,在每一個隊列中,剛進入的請求和相鄰請求進行合併。
CFQ調度程序以時間片輪轉調度隊列,從每一個隊列中選取請求固定數字的操做,默認爲4,而後進入下一輪調度。這樣在進程級實現了公平。
目前內核默認的調度算法是CFQ。
第五種算法 空操做
之因此這樣命名,是由於這種算法基本不做什麼事情,基本就是先進先出,固然,若是相鄰的操做可以合併,仍是會合並,空操做懶惰是有道理的,由於這種算法是用在閃存設備上,若是設備沒有尋址負擔,那麼也沒有必要對其排序。
11、頁高速緩存和頁回寫
咱們仍是經過解答幾個問題,來了解頁高速緩存和頁回寫。
第一個問題,爲何會有頁高速緩存
這個主要緣由是由於內存和磁盤的速度差距很是大,磁盤的讀寫速度是毫秒級別的,內存的讀寫速度是納秒級別的,若是可以經過內存緩存磁盤數據,就能夠大大提升系統速度。另外,被訪問的數據,頗有可能再次被訪問,若是可以把數據緩存到內存中,那麼數據若是再次被頻繁訪問,就能夠提升系統性能。
第二個問題,寫磁盤如何緩存
寫緩存有三種方式,第一種是不緩存,就是當寫數據時,直接寫到磁盤,這種方式數據最安全,可是性能最低。第二種方式是透寫,數據先寫到緩存,而後馬上寫磁盤,這樣數據也是很安全,可是性能也比較低。第三種方式是回寫,writeback,數據寫到緩存,就認爲成功,到必定時間,或者數據比較多的時候,再寫盤,這種方式性能很好,可是若是數據在緩存中的時候,機器忽然斷電,有可能數據丟失。
第三個問題,讀緩存的回收策略是什麼
由於內存有限,不可能把整個磁盤的數據緩存到內存中,只能保證把比較熱的數據緩存起來,那麼如何確認數據比較熱呢,有兩種算法,一種是根據時間,系統掃描頁面,沒有被訪問的,時間比較久的頁面,就會被釋放掉,還有一種算法,是雙鏈表,或者多鏈表,增長了一些統計的概念,更精確一些。
第四個問題,筆記本電腦模式
在筆記本電腦上,由於有電池,同時爲了提高性能,通常啓用的都是回寫模式,而且刷新磁盤的時間間隔更長,這樣還能夠省電。目前的大部分系統也能夠在筆記本電腦啓用電池時,自動修改回寫策略。
另外也能夠執行命令sync,強制系統刷盤。通常在我的版的系統上,默認都是開啓回寫,這樣性能會好不少,可是在服務器系統上,通常默認都是透寫模式,由於在服務器上,數據更重要。這也是爲何有時候你會發現,在我的PC上,磁盤寫性能竟然要好於服務器的緣由。
12、關於內核的幾個概念
第一個概念,Linux的設備類型
在Linux及Unix中,設備被分爲三種類型:
塊設備
字符設備
網絡設備
塊設備縮寫爲blkdev,塊設備以塊爲單位,而且是能夠尋址的,便可以隨機訪問任何位置的數據。塊設備一般被掛載爲文件系統來使用。
字符設備縮寫爲cdev,字符設備不可尋址,只能流式訪問,與塊設備不一樣,應用程序一般直接和塊設備交互。
網絡設備一般是經過物理設備和IP協議提供的,網絡設備打破了unix一切皆文件的設計原則,對網絡設備的訪問是經過套接字API實現的。
Linux還提供了其餘設備類型,但都是針對單個任務,而非通用的。
另外,並非全部設備驅動都表示物理設備,有些設備驅動是虛擬的,稱之爲「僞設備」,最多見的是內核隨機數發生器/dev/urandom,空設備/dev/null,零設備/dev/zero等。
儘快Linux內核是單塊內核的操做系統,可是整個內核是模塊化的,容許在運行時動態的插入或者刪除代碼,即所謂的可裝載內核模塊。
第二個概念,內核的可移植性
Linux是可移植性很是好的操做系統,支持許多不一樣體系的計算機。可移植性是指操做系統代碼從一套體系遷移到另一套體系的方便程度。
在操做系統可移植性方面,設計有兩種思路。
一種思路是儘可能追求通用性,儘可能少的使用匯編語言,這樣設計出來的操做系統可移植性很是高,可是缺點是不能針對某種體系深刻優化。
還有一種思路就是基本不考慮可移植性,只對一種體系深度優化,Windows系統就是這樣的系統,主要就是針對x86系統優化,可是可移植性極差。
Linux系統走了一條中間道路,差很少全部的接口和核心代碼都是獨立於硬件的,可是,對於性能要求很嚴格的部分,內核會針對不一樣體系調整,這使得linux在可移植性和性能之間取得比較好的平衡。
第三個概念 社區
你們都知道,linux是開源的,社區和代碼隨時能夠訪問,只要有興趣,也能夠隨時參與社區活動。可是linux入門門檻比較高。須要一個比較長的過程,只要堅持,最終會跨過這個門檻。
後記
本文經過十二部分蜓蜓點水式的介紹,但願可以幫助你們能記住並理解幾個概念。若是有興趣更深刻的瞭解,推薦閱讀下《Linux內核設計與實現》這本書。