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