我們一起聊聊操作系統(tǒng)
一、計(jì)算機(jī)簡(jiǎn)介
在講解操作系統(tǒng)之前,我們先從整體上講一下計(jì)算機(jī),再?gòu)挠布v到軟件,最后再講操作系統(tǒng)。
1.1 什么是計(jì)算機(jī)
我們生活中幾乎到處都能接觸到計(jì)算機(jī),從我們?nèi)粘J褂玫氖謾C(jī)、平板,到辦公使用的筆記本、臺(tái)式機(jī),到銀行的ATM機(jī),到各處可見(jiàn)的監(jiān)控設(shè)備,還有我們平時(shí)看不見(jiàn)但是我們?yōu)g覽的網(wǎng)頁(yè)其所在的服務(wù)器,還有微信、抖音等我們?nèi)粘K玫腁PP它們所在的服務(wù)器,等等,這些都是計(jì)算機(jī)。如果沒(méi)有了計(jì)算機(jī),我們的生活將難以想象。那么究竟什么是計(jì)算機(jī)呢,這個(gè)還真不好下定義的,那我們就來(lái)看一下百度百科對(duì)計(jì)算機(jī)的定義:計(jì)算機(jī)俗稱電腦,是現(xiàn)代一種用于高速計(jì)算的電子計(jì)算機(jī)器,可以進(jìn)行數(shù)值計(jì)算,又可以進(jìn)行邏輯計(jì)算,還具有存儲(chǔ)記憶功能。是能夠按照程序運(yùn)行,自動(dòng)、高速處理海量數(shù)據(jù)的現(xiàn)代化智能電子設(shè)備。計(jì)算機(jī)的應(yīng)用非常廣泛,從我們?nèi)粘W畛R?jiàn)的臺(tái)式機(jī)、筆記本到手機(jī)平板都是計(jì)算機(jī),而且大到服務(wù)器、超級(jí)計(jì)算機(jī),小到各種嵌入式設(shè)備也都是計(jì)算機(jī)?,F(xiàn)在我們對(duì)計(jì)算機(jī)既有了感性的認(rèn)識(shí),又知道了的它的權(quán)威定義,那么計(jì)算機(jī)是怎么產(chǎn)生的呢,下面我們來(lái)看一看計(jì)算機(jī)的發(fā)展史。
1.2 計(jì)算機(jī)發(fā)展史
計(jì)算機(jī)的發(fā)展史從概念上最早可以追溯到結(jié)繩計(jì)數(shù)、手指頭計(jì)數(shù),有了計(jì)數(shù)然后我們就有了加減乘除各種計(jì)算需求。但是人類天然的計(jì)算能力不太行,于是便開(kāi)始創(chuàng)造工具、使用工具進(jìn)行計(jì)算。最古老的計(jì)算工具當(dāng)數(shù)中國(guó)的算盤(pán)了,算盤(pán)運(yùn)用熟練的話可以使加減運(yùn)算的速度大大加快。后來(lái)歐洲人發(fā)明了計(jì)算尺,通過(guò)對(duì)數(shù)方式把乘除運(yùn)算轉(zhuǎn)換為加減運(yùn)算,使得乘除的計(jì)算速度有了很大的飛躍。歐洲在文藝復(fù)興和科學(xué)革命之后,很多科學(xué)家都想發(fā)明一種通過(guò)齒輪和力學(xué)原理來(lái)驅(qū)動(dòng)的機(jī)械計(jì)算機(jī),最出名的當(dāng)數(shù)帕斯卡發(fā)明的加法器了,可以進(jìn)行6位數(shù)的加減運(yùn)算。與牛頓同時(shí)期的著名數(shù)學(xué)家、哲學(xué)家萊布尼茨在帕斯卡之后發(fā)明了乘法器,可以進(jìn)行加減乘除開(kāi)方運(yùn)算。萊布尼茨的理想遠(yuǎn)不止于此,他一直想把邏輯思維轉(zhuǎn)化為數(shù)學(xué)運(yùn)算,并且用機(jī)器實(shí)現(xiàn)邏輯運(yùn)算。雖然在當(dāng)時(shí)沒(méi)有實(shí)現(xiàn),但是為后來(lái)的計(jì)算機(jī)發(fā)展提供了很大的啟發(fā)。據(jù)說(shuō)萊布尼茨看了中國(guó)的易經(jīng)八卦之后從中發(fā)現(xiàn)了二進(jìn)制,并認(rèn)為以后的計(jì)算機(jī)可能會(huì)采取二進(jìn)制來(lái)實(shí)現(xiàn)。
19世紀(jì)早期的英國(guó)科學(xué)家巴貝奇發(fā)明了差分機(jī),差分機(jī)可以進(jìn)行一些非常復(fù)雜的數(shù)學(xué)運(yùn)算,如多項(xiàng)式計(jì)算、求解三角函數(shù)等。后來(lái)他又設(shè)計(jì)出了分析機(jī),但是由于分析機(jī)過(guò)于復(fù)雜,在當(dāng)時(shí)并沒(méi)有制造出來(lái)。雖然分析機(jī)沒(méi)有造出來(lái),但是它的意義卻是非常巨大的。它的內(nèi)部有存儲(chǔ)部分,有碾磨(相當(dāng)于現(xiàn)在的CPU),還有輸入輸出部分,已經(jīng)可以看到現(xiàn)代計(jì)算機(jī)的影子了。而且巴貝奇是第一個(gè)意識(shí)到計(jì)算機(jī)是需要編程的人,在此之前人們一直覺(jué)得計(jì)算機(jī)如果造出來(lái)了,它就可以做任何它能做的事情,并不需要人類為它編程,當(dāng)然之前的人也意識(shí)不到編程是什么意思。英國(guó)著名詩(shī)人拜倫的女兒Ada,自幼對(duì)數(shù)學(xué)興趣濃厚,與科學(xué)家交往甚密。后來(lái)在其它人的介紹下認(rèn)識(shí)了巴貝奇,兩人很快就成為了亦師亦友的關(guān)系。雖然分析機(jī)并沒(méi)有造出來(lái),但是Ada還是為分析機(jī)寫(xiě)了很多程序,所以被后世稱為歷史上第一個(gè)程序員。估計(jì)絕大多數(shù)人都想不到世界上第一個(gè)程序員竟然是位女程序員。后來(lái)美國(guó)國(guó)防部設(shè)計(jì)了一種計(jì)算機(jī)語(yǔ)言,用的就是Ada的名字來(lái)命名的。
然后計(jì)算機(jī)從機(jī)械時(shí)代到了機(jī)電時(shí)代,很快又到了電子時(shí)代。1946年2月14日,被認(rèn)為是世界上第一臺(tái)電子計(jì)算機(jī)的ENIAC誕生了,之后計(jì)算機(jī)的發(fā)展就走上了快車道,從電子管到晶體管到集成電路到大規(guī)模集成電路,一直發(fā)展至今。這一段歷史比較清晰,網(wǎng)上的資料也很多,而且這段歷史中計(jì)算機(jī)的邏輯結(jié)構(gòu)并沒(méi)有發(fā)生多大的變化,所以我們就不展開(kāi)細(xì)說(shuō)了。
1.3 計(jì)算機(jī)的二元結(jié)構(gòu)
計(jì)算機(jī)是由硬件和軟件組成的,就像人是由肉體和靈魂組成的一樣。計(jì)算機(jī)的各種硬件就像是人的大腦、心肝脾肺腎、骨骼、皮膚一樣,支撐著人活著。但是人要是只有肉體沒(méi)有靈魂,那就是植物人,電腦要是只有硬件沒(méi)有軟件,那就是一堆金屬和塑料,沒(méi)有用處。下面兩章我們分別講一下計(jì)算機(jī)的硬件體系結(jié)構(gòu)和軟件體系結(jié)構(gòu)。
二、計(jì)算機(jī)硬件體系結(jié)構(gòu)
我們按照從簡(jiǎn)單到復(fù)雜、從抽象到具體的過(guò)程來(lái)講一講計(jì)算機(jī)的硬件體系結(jié)構(gòu)。
2.1 圖靈機(jī)模型
什么是圖靈機(jī),我們?yōu)槭裁匆v圖靈機(jī)?圖靈機(jī)是計(jì)算機(jī)的數(shù)學(xué)模型,我們?cè)谶@里講圖靈機(jī)并不是為了學(xué)習(xí)圖靈機(jī)本身,圖靈機(jī)的理論還是很復(fù)雜的。我們這里講圖靈機(jī)就是為了理解一下計(jì)算機(jī)的運(yùn)行模型,明白CPU的運(yùn)行是線性的。我們先來(lái)看一下圖靈機(jī)的定義,圖靈機(jī)是英國(guó)數(shù)學(xué)家阿蘭·圖靈于1936年提出的一種抽象的計(jì)算機(jī)模型,即將人們使用紙筆進(jìn)行數(shù)學(xué)運(yùn)算的過(guò)程進(jìn)行抽象,由一個(gè)虛擬的機(jī)器替代人類進(jìn)行數(shù)學(xué)運(yùn)算。它有一條無(wú)限長(zhǎng)的紙帶,紙帶分成了一個(gè)一個(gè)的小方格,每個(gè)方格有不同的內(nèi)容。有一個(gè)機(jī)器頭在紙帶上移來(lái)移去。機(jī)器頭有一組內(nèi)部狀態(tài),還有一些固定的程序。在每個(gè)時(shí)刻,機(jī)器頭都要從當(dāng)前紙帶上讀入一個(gè)方格信息,然后結(jié)合自己的內(nèi)部狀態(tài)查找程序表,根據(jù)程序把信息輸出到紙帶方格上,并轉(zhuǎn)換自己的內(nèi)部狀態(tài),然后進(jìn)行移動(dòng)。我們可以看出圖靈機(jī)的定義還是很簡(jiǎn)單的,圖靈機(jī)的運(yùn)行模式和我們現(xiàn)在的CPU運(yùn)行模式還是很像的,我們下面畫(huà)一個(gè)圖靈機(jī)的運(yùn)行模型。
從圖中我們可以看到圖靈機(jī)模型確實(shí)非常簡(jiǎn)單,有一個(gè)讀寫(xiě)磁頭,磁頭有自己的內(nèi)部狀態(tài),磁頭根據(jù)自己的內(nèi)部狀態(tài)會(huì)讀或者寫(xiě)當(dāng)前方格里面的內(nèi)容,然后相應(yīng)地改變自己的狀態(tài),然后向左或者向右移動(dòng)一格或者若干格繼續(xù)執(zhí)行。圖靈機(jī)本身的理論非常復(fù)雜,想學(xué)習(xí)的同學(xué)可以去搜索一下或者查閱相關(guān)的書(shū)籍。我們?cè)谶@里想要說(shuō)的就一點(diǎn),圖靈機(jī)的運(yùn)行是線性的,線性的意思不是說(shuō)它是單向直線性的,而是說(shuō)它只能在一條線上運(yùn)行,而且所有運(yùn)行的點(diǎn)按時(shí)間記錄排序是線性的。
2.2 馮諾依曼結(jié)構(gòu)
馮諾依曼結(jié)構(gòu)是計(jì)算機(jī)的物理模型,是計(jì)算機(jī)能夠制造出來(lái)的關(guān)鍵。馮諾依曼結(jié)構(gòu)由三部分組成:一是計(jì)算機(jī)硬件實(shí)現(xiàn)上采用二進(jìn)制;二是存儲(chǔ)程序設(shè)計(jì),就是把計(jì)算機(jī)程序放到存儲(chǔ)器中,關(guān)于這一部分的論述就參看《深入理解編譯體系》的第一章;三是計(jì)算機(jī)由控制器、運(yùn)算器、存儲(chǔ)器、輸入設(shè)備、輸出設(shè)備五個(gè)部分組成??刂破魇钦麄€(gè)計(jì)算機(jī)的神經(jīng)中樞,控制著整個(gè)計(jì)算機(jī)的運(yùn)轉(zhuǎn)??刂破鲝拇鎯?chǔ)器中取指令,解析指令,然后根據(jù)指令的內(nèi)容讓運(yùn)算器進(jìn)行相應(yīng)的運(yùn)算或者向其它部件發(fā)出一些命令。運(yùn)算器是負(fù)責(zé)進(jìn)行數(shù)學(xué)運(yùn)算和邏輯運(yùn)算的部件,數(shù)學(xué)運(yùn)算包括加減乘除移位比較等運(yùn)算,邏輯運(yùn)算包括與、或、非、異或等運(yùn)算。存儲(chǔ)器是存儲(chǔ)程序和數(shù)據(jù)的地方。輸入設(shè)備和輸出設(shè)備是負(fù)責(zé)和人交互的設(shè)備,人們通過(guò)輸入設(shè)備向計(jì)算機(jī)輸入命令和數(shù)據(jù),通過(guò)輸出設(shè)備得到計(jì)算機(jī)運(yùn)算出來(lái)的結(jié)果。下面我們畫(huà)一張馮諾依曼結(jié)構(gòu)的圖。
可以看到馮諾依曼結(jié)構(gòu)比圖靈機(jī)模型復(fù)雜了不少,但是總體上還是很簡(jiǎn)單的。有了馮諾依曼結(jié)構(gòu),計(jì)算機(jī)就有了被造出來(lái)的物理模型,我們也可以從馮諾依曼結(jié)構(gòu)中更好地去理解計(jì)算機(jī)的邏輯結(jié)構(gòu)。
2.3 現(xiàn)代計(jì)算機(jī)結(jié)構(gòu)
其實(shí)并沒(méi)有現(xiàn)代計(jì)算機(jī)結(jié)構(gòu)這個(gè)術(shù)語(yǔ),現(xiàn)代計(jì)算機(jī)結(jié)構(gòu)是我把馮諾依曼結(jié)構(gòu)再具體化一下,把具體計(jì)算機(jī)結(jié)構(gòu)再抽象化一下總結(jié)出來(lái)的,可以讓大家更方便更深入地理解計(jì)算機(jī)硬件體系結(jié)構(gòu)?,F(xiàn)代計(jì)算機(jī)和馮諾依曼結(jié)構(gòu)最主要的區(qū)別是把控制器和運(yùn)算器合二為一稱作CPU,把存儲(chǔ)器一分為二,分為內(nèi)存和外存。我們來(lái)畫(huà)一下現(xiàn)代計(jì)算機(jī)結(jié)構(gòu)的圖。
現(xiàn)代計(jì)算機(jī)的具體體系結(jié)構(gòu)非常復(fù)雜,我只是撿其中比較關(guān)鍵的畫(huà)了出來(lái),很多細(xì)節(jié)沒(méi)有畫(huà),如總線、南橋、北橋、各種控制器等,上圖中也有很多連線沒(méi)有畫(huà)。計(jì)算機(jī)運(yùn)行的時(shí)候先從外存中加載程序到內(nèi)存,然后CPU不斷地執(zhí)行內(nèi)存中的指令、讀寫(xiě)內(nèi)存數(shù)據(jù),CPU也會(huì)使內(nèi)存繼續(xù)從外存中讀取數(shù)據(jù)或者把內(nèi)存的數(shù)據(jù)更新到外存。CPU通過(guò)IO端口或者內(nèi)存映射IO來(lái)控制外設(shè)獲取外設(shè)的狀態(tài)或者數(shù)據(jù),外設(shè)通過(guò)中斷控制器(APIC)向CPU通知報(bào)告一些事情。計(jì)算機(jī)就是這樣不斷運(yùn)行的。
三、計(jì)算機(jī)軟件體系結(jié)構(gòu)
從上面我們知道了硬件的整體結(jié)構(gòu)和運(yùn)行情況,下面我們來(lái)說(shuō)一說(shuō)計(jì)算機(jī)的軟件體系結(jié)構(gòu)。計(jì)算機(jī)軟件整體上分為系統(tǒng)軟件和應(yīng)用軟件,系統(tǒng)軟件是面向硬件和面向程序員的軟件,應(yīng)用軟件是面向普通用戶的軟件。
3.1 系統(tǒng)軟件
最重要的系統(tǒng)軟件是操作系統(tǒng),操作系統(tǒng)是管理所有硬件并為進(jìn)程提供運(yùn)行環(huán)境的軟件,所有的應(yīng)用程序都要運(yùn)行在操作系統(tǒng)之上,操作系統(tǒng)為應(yīng)用程序提供服務(wù),并管理所有的應(yīng)用程序。關(guān)于操作系統(tǒng)的具體情況,請(qǐng)看第四章和第五章。
在操作系統(tǒng)啟動(dòng)之前還有兩個(gè)軟件,它們是Boot Firmware 和 BootLoader,Boot Firmware的存在有一個(gè)原因、兩個(gè)作用:一個(gè)原因是為了克服計(jì)算機(jī)啟動(dòng)時(shí)雞生蛋、蛋生雞的矛盾,按照馮諾依曼模型,計(jì)算機(jī)啟動(dòng)時(shí)要從內(nèi)存讀取程序運(yùn)行程序,而計(jì)算機(jī)啟動(dòng)時(shí)內(nèi)存是空的啥都沒(méi)有,要想讓內(nèi)存有程序就必須要從外存中加載程序,而從外存中加載程序的指令也是程序,這個(gè)程序也要先加載到內(nèi)存,怎么辦呢,我們提前寫(xiě)好一段程序,把它固化到ROM中去,ROM是掉電不會(huì)丟失內(nèi)容的,再把這個(gè)ROM配置到特定的物理內(nèi)存地址空間中,具體地址是什么要看CPU廠商了。CPU啟動(dòng)時(shí)會(huì)從特定的內(nèi)存地址運(yùn)行,也就是這個(gè)ROM所在地址,這樣CPU就有程序可以執(zhí)行了。Boot Firmware的兩個(gè)作用分別是初始化硬件檢測(cè)硬件和加載操作系統(tǒng)為操作系統(tǒng)提供一些基本的硬件信息。后來(lái)為了靈活性,Boot Firmware并不是直接加載的操作系統(tǒng),而是先加載BootLoader再由BooLoader加載的操作系統(tǒng)。BootLoader的存在一是可以很方便配置一些信息如OS啟動(dòng)參數(shù),二是支持多操作系統(tǒng)啟動(dòng)。桌面計(jì)算機(jī)常見(jiàn)的Boot Firmware是BIOS和UEFI,BIOS是PC最初的Boot Firmware,由于推出的時(shí)間比較早,所以存在很多問(wèn)題,后來(lái)Intel又重新設(shè)計(jì)開(kāi)發(fā)了UEFI,UEFI相比BIOS由很多的優(yōu)點(diǎn)?,F(xiàn)在基本上大部分桌面計(jì)算機(jī)都已經(jīng)轉(zhuǎn)向了UEFI。比較常見(jiàn)的Boot Loader如LILO、GRUB,現(xiàn)在桌面版Linux用的基本都是GRUB。
嵌入式系統(tǒng)的概念和這個(gè)不太一樣,嵌入式系統(tǒng)最先啟動(dòng)的兩個(gè)軟件叫做Boot ROM和BootLoader,Boot ROM存在的原因和Boot Firmware存在的原因是一樣的,但是Boot ROM并不具有Boot Firmware的功能,Boot ROM是比較輕的,相當(dāng)于是UEFI/PI 中的SEC階段,Boot Firmware中的其它功能被轉(zhuǎn)移到BootLoader中來(lái)做,Boot ROM一般是由CPU廠商來(lái)開(kāi)發(fā)的,不開(kāi)源。BootLoader負(fù)責(zé)硬件初始化并直接加載啟動(dòng)操作系統(tǒng)。造成桌面計(jì)算機(jī)和嵌入式系統(tǒng)的這種差異的原因是兩者的運(yùn)行和使用環(huán)境大不相同造成的。
作為最基礎(chǔ)的三個(gè)系統(tǒng)軟件,Boot Firmware、BootLoader、操作系統(tǒng),前兩者很少有人熟悉,甚至很多人根本就沒(méi)聽(tīng)說(shuō)過(guò),是因?yàn)樗鼈儐?dòng)過(guò)去之后基本就不再起作用了。除此之外還有一套很重要的系統(tǒng)軟件,它是編譯工具鏈,包括預(yù)處理器、編譯器、匯編器、鏈接器,所有的軟件要想從源碼變成二進(jìn)制程序都需要它們來(lái)處理,想要具體了解它們的情況請(qǐng)參看《深入理解編譯系統(tǒng)》。除此之外還有一些系統(tǒng)查看、配置、維護(hù)工具也算是系統(tǒng)軟件,如ps、top、sysctl等。
3.2 應(yīng)用軟件
應(yīng)用軟件就比較好理解了,就是那些面向普通用戶實(shí)現(xiàn)某些特定需求的軟件,如 上網(wǎng)-瀏覽器,聽(tīng)歌-千千靜聽(tīng),看視頻-暴風(fēng)影音,追劇-愛(ài)奇藝,社交-微信,購(gòu)物-淘寶,刷視頻-抖音。所有應(yīng)用軟件都運(yùn)行在操作系統(tǒng)之上,享受操作系統(tǒng)的服務(wù),并接受操作系統(tǒng)的管理。
四、操作系統(tǒng)組成結(jié)構(gòu)
我們經(jīng)常說(shuō)操作系統(tǒng),那么究竟什么是操作系統(tǒng)呢,我們先來(lái)從操作系統(tǒng)的構(gòu)成成分的角度來(lái)講一講。操作系統(tǒng)是由三部分構(gòu)成的,其中最重要的是內(nèi)核部分,但是內(nèi)核并不是操作系統(tǒng)的全部,除了內(nèi)核外,OS庫(kù)和OS進(jìn)程也是操作系統(tǒng)的一部分。OS庫(kù)是運(yùn)行在用戶空間進(jìn)程中的共享庫(kù),雖然它是運(yùn)行在用戶空間,雖然它只是共享庫(kù),但是它是操作系統(tǒng)的一部分,因?yàn)樗鼌⑴c實(shí)現(xiàn)了操作系統(tǒng)的一部分功能。OS進(jìn)程雖然是運(yùn)行在用戶空間的進(jìn)程,但是它并不屬于應(yīng)用程序,因?yàn)樗菍?shí)現(xiàn)操作系統(tǒng)功能必不可少的一部分。
4.1 內(nèi)核
內(nèi)核是操作系統(tǒng)最重要的部分,是操作系統(tǒng)的核心,它運(yùn)行在內(nèi)核空間,運(yùn)行在CPU特權(quán)模式,直接與硬件打交道,并直接管理著進(jìn)程的運(yùn)行。內(nèi)核可以分為宏內(nèi)核與微內(nèi)核,宏內(nèi)核就是傳統(tǒng)的內(nèi)核方式,所有的內(nèi)核功能都放在內(nèi)核空間里。微內(nèi)核是把一些最基本的無(wú)法移出內(nèi)核的功能才放在內(nèi)核里,其它能放到用戶空間的功能全都放到用戶空間進(jìn)程里,叫做微服務(wù),應(yīng)用進(jìn)程通過(guò)IPC與微服務(wù)溝通,微服務(wù)與微內(nèi)核一起構(gòu)成一個(gè)完成的內(nèi)核。
微內(nèi)核辯論:
我對(duì)微內(nèi)核的態(tài)度是和Linus是一樣的,下面的話是節(jié)選自Linus寫(xiě)的書(shū)《Just For Fun》。
微內(nèi)核的理論依據(jù)是,操作系統(tǒng)是非常復(fù)雜的,所以要通過(guò)模式化來(lái)減少?gòu)?fù)雜性。微內(nèi)核方法的原則,即核心的核心,是盡量減少功能。它的主要功能是傳播。電腦所提供的一系列不同的服務(wù)都是通過(guò)微內(nèi)核的傳播渠道實(shí)現(xiàn)的。因此,應(yīng)盡量分割問(wèn)題的空間,使其不再?gòu)?fù)雜。我認(rèn)為這種做法很愚蠢。是的,每一個(gè)單獨(dú)的部分是簡(jiǎn)單的,但是相互作用的多種功能如果放在一起就要復(fù)雜得多 ,而 Linux 就是后者的情況。想一想自己的大腦。每一個(gè)單獨(dú)的部分都很簡(jiǎn)單,但是各部分的相互作用構(gòu)成了一個(gè)復(fù)雜的系統(tǒng)。這是一個(gè)整體比個(gè)別更大的問(wèn)題。拿一個(gè)問(wèn)題來(lái)說(shuō),如果你簡(jiǎn)單地將問(wèn)題一分為二,說(shuō)半個(gè)問(wèn)題要容易一半,那么你就忽略了一個(gè)事實(shí),即:你必須要考慮到兩個(gè)半個(gè)之間的聯(lián)系所帶來(lái)的復(fù)雜性。微內(nèi)核的理論是,如果把核分為五十份,那么每一份都只有五十分之一的復(fù)雜性。但是每個(gè)人都忽視了一個(gè)事實(shí),即各部分之間的聯(lián)系事實(shí)上比源系統(tǒng)更加復(fù)雜,而且那些個(gè)別部分也不是那么簡(jiǎn)單。這是我對(duì)微內(nèi)核最重要的反駁:你想實(shí)現(xiàn)的簡(jiǎn)單化是錯(cuò)誤的簡(jiǎn)單化。
上面是Linus的分析,我是非常贊同的,內(nèi)核的復(fù)雜是內(nèi)核的固有屬性,是它內(nèi)在邏輯的復(fù)雜,你把它人為的劃分到不同的用戶空間進(jìn)程,并沒(méi)有減少它內(nèi)在的邏輯復(fù)雜性。把一個(gè)事物模塊化能減少它的復(fù)雜性,但是模塊化不是說(shuō)非得要在形式上把它們分割到不同的進(jìn)程,在邏輯上把它們分割開(kāi)來(lái)就可以了。比如說(shuō)有一個(gè)進(jìn)程非常復(fù)雜,你把它弄成一坨不分模塊是不對(duì)的,但是模塊化的形式可以是把它分割成一個(gè)exe和n個(gè)so就可以了,不一定非要把它做成n+1個(gè)進(jìn)程。n+1個(gè)模塊無(wú)論是在進(jìn)程內(nèi)交互,還是在進(jìn)程間交互,其本身的復(fù)雜性并沒(méi)有減少,進(jìn)程間通信還降低了效率。
微內(nèi)核還有一個(gè)經(jīng)常宣傳的優(yōu)點(diǎn)是如果某個(gè)微服務(wù)出了問(wèn)題,只重啟這個(gè)微服務(wù)就可以了,不用重啟整個(gè)內(nèi)核。但是這個(gè)優(yōu)點(diǎn)是禁不起推敲的,以我在安卓系統(tǒng)上的工作經(jīng)驗(yàn)為例,如果某個(gè)重要的JAVA進(jìn)程崩潰重啟了比如SystemServer,就會(huì)導(dǎo)致很多Java進(jìn)程包括所有的APK進(jìn)程都會(huì)重啟,雖然內(nèi)核沒(méi)有重啟,但是在用戶看來(lái)還是整個(gè)系統(tǒng)都重啟了。所以如果某個(gè)微服務(wù)重啟了,一定會(huì)導(dǎo)致所有的應(yīng)用進(jìn)程重啟的,在用戶看來(lái)還是整個(gè)系統(tǒng)重啟了,也許你會(huì)說(shuō)微內(nèi)核本身并沒(méi)有重啟,所以整個(gè)重啟時(shí)間減少了,但是這點(diǎn)時(shí)間減少并沒(méi)有多少意義。而微內(nèi)核帶來(lái)的整個(gè)系統(tǒng)性能的大幅度下降這個(gè)問(wèn)題,則是無(wú)法容忍和無(wú)法克服的。
我發(fā)現(xiàn)很多嵌入式系統(tǒng)使用的都是微內(nèi)核。因?yàn)榍度胧较到y(tǒng)是一個(gè)相對(duì)封閉的系統(tǒng),不會(huì)有經(jīng)常下載新程序、突然運(yùn)行很多進(jìn)程的情況,所以嵌入式系統(tǒng)對(duì)性能的需求是穩(wěn)定的。所以微內(nèi)核只要在測(cè)試中能滿足嵌入式系統(tǒng)的性能需求,問(wèn)題就不大。通用操作系統(tǒng)包括手機(jī)系統(tǒng)、桌面系統(tǒng)、服務(wù)器系統(tǒng),都有面臨負(fù)載突然暴增的情況,所以對(duì)性能的要求非常高。
目前所有流行的通用操作系統(tǒng)內(nèi)核都是宏內(nèi)核,有些宣稱自己是微內(nèi)核或者混合內(nèi)核的,本質(zhì)上還是宏內(nèi)核?!禡ac OS X Internals A Systems Approach》6.2章節(jié)明確說(shuō)XNU不是微內(nèi)核,是宏內(nèi)核,《Mac OS X and iOS Internals》第8章Hybrid Kernels這一節(jié)說(shuō),Windows、MacOS雖然是混合內(nèi)核,但是實(shí)際上還是宏內(nèi)核。
4.2 OS庫(kù)
有些運(yùn)行在用戶空間的動(dòng)態(tài)庫(kù),是屬于操作系統(tǒng)的一部分,這些庫(kù)雖然是庫(kù),但是并不是由應(yīng)用程序開(kāi)發(fā)者開(kāi)發(fā),而是由操作系統(tǒng)廠商開(kāi)發(fā),因?yàn)樗鼈兪菢?gòu)成操作系統(tǒng)功能的一部分。在Linux上常見(jiàn)的OS庫(kù)有l(wèi)ibc.so、ld-linux-x86-64.so、libm.so、libpthread.so、libdl.so、librt.so、libcrypt.so、libpam.so、libX11.so等。Linux上的OS庫(kù)是非常多的,上面只是隨便舉幾個(gè)例子,下面我們對(duì)其中最重要的幾個(gè)進(jìn)行一下分析。
libc.so,libc(C Library)的二進(jìn)制動(dòng)態(tài)庫(kù)形式,它的源碼實(shí)現(xiàn)有很多,Linux發(fā)行版一般用的是glibc,安卓上的libc的實(shí)現(xiàn)叫做bionic。libc不僅生成libc.so,還有l(wèi)ibpthread.so、ld-linux-x86-64.so等很多庫(kù)都是libc生成的。libc.so里面不僅有C標(biāo)準(zhǔn)庫(kù)的實(shí)現(xiàn),還有對(duì)系統(tǒng)調(diào)用的封裝,所以libc.so不是一個(gè)普通的庫(kù),它是內(nèi)核的對(duì)外接口庫(kù),是非常重要的,是進(jìn)程運(yùn)行必不可少的。你cat 任何一個(gè)進(jìn)程的 /proc/pid/maps 幾乎一定會(huì)看到libc.so,如果看不到,那是因?yàn)橛行┻M(jìn)程是把libc靜態(tài)鏈接到自身了,進(jìn)程里面還是包含libc的。
ld-linux-x86-64.so,是Linux系統(tǒng)的加載器,負(fù)載在進(jìn)程啟動(dòng)時(shí)把進(jìn)程所依賴的所有動(dòng)態(tài)庫(kù)都加載到內(nèi)存并進(jìn)行一些重定位工作。沒(méi)有了ld-linux-x86-64.so,幾乎所有的應(yīng)用進(jìn)程都無(wú)法啟動(dòng)運(yùn)行。
libpthread.so,Linux的多線程實(shí)現(xiàn)庫(kù),想要使用系統(tǒng)的多線程功能的進(jìn)程必須要調(diào)用這個(gè)so才行。
4.3 OS進(jìn)程
并不是所有的操作系統(tǒng)功能都放進(jìn)了內(nèi)核里,還有很多功能是放在了用戶進(jìn)程里來(lái)實(shí)現(xiàn)的,我們把這些進(jìn)程叫做OS進(jìn)程。我總結(jié)了一些常見(jiàn)的放在用戶空間的操作系統(tǒng)功能,它們是 init system、package system、window system。這些都被叫做某某系統(tǒng),可見(jiàn)它們并不是簡(jiǎn)單的一個(gè)進(jìn)程而已,它們一般是由一個(gè)進(jìn)程加上一些配置文件,有的還提供一些so,它們有著自己的邏輯體系,下面我們來(lái)一一講解一下。
Init system,是操作系統(tǒng)啟動(dòng)的第一個(gè)用戶空間進(jìn)程,它負(fù)責(zé)把整個(gè)用戶空間給啟動(dòng)起來(lái),啟動(dòng)系統(tǒng)所有的守護(hù)進(jìn)程,設(shè)置一些系統(tǒng)配置,在系統(tǒng)運(yùn)行的過(guò)程中監(jiān)管著整個(gè)系統(tǒng),系統(tǒng)的關(guān)機(jī)也由它負(fù)責(zé)。最早的init system 叫 sysv init,它的實(shí)現(xiàn)比較簡(jiǎn)單直接方便,大量使用腳本。后來(lái)sysv init 變成了系統(tǒng)啟動(dòng)速度慢的最大瓶頸,一是因?yàn)槟_本的運(yùn)行比較慢,二是因?yàn)樗菃尉€程的,沒(méi)能發(fā)揮出多核的優(yōu)勢(shì)。后面就出現(xiàn)了兩個(gè)init system,分別是upstart 和 systemd 來(lái)解決sysv init 的問(wèn)題,現(xiàn)在大部分linux發(fā)行版都把init system 換成了systemd。
Package system,一個(gè)系統(tǒng)上有那么多的軟件,該如何安裝、如何卸載、如何管理,安裝包該采取什么格式,這便是package system要做的工作。目前Linux上有兩個(gè)比較流行的package system,一個(gè)是redhat 推出的rpm,一個(gè)是debian推出的dpkg。
Window system 也叫做Graphics system,也就是一個(gè)操作系統(tǒng)的圖形界面該如何管理,這是一個(gè)非常復(fù)雜的系統(tǒng),牽涉面非常廣,為什么是Android呢,因?yàn)槲以谑謾C(jī)廠商工作的時(shí)候做過(guò)這方面的工作,相對(duì)比較熟悉,對(duì)Linux的圖形系統(tǒng)不太熟悉。
除了上面三個(gè)很大的系統(tǒng)之外,還有一些GUI程序,看起來(lái)像是應(yīng)用程序,但是實(shí)際上應(yīng)該劃到操作系統(tǒng)的內(nèi)容里面,比如桌面、文件管理器、設(shè)置等程序,因?yàn)樗鼈兪呛筒僮飨到y(tǒng)緊密相關(guān)的,而且一般也是由操作系統(tǒng)廠商來(lái)開(kāi)發(fā)和維護(hù)的。
五、操作系統(tǒng)本質(zhì)解析
前面我們對(duì)操作系統(tǒng)的組成結(jié)構(gòu)進(jìn)行了分析,下面我們從操作系統(tǒng)的功能作用的角度再對(duì)操作系統(tǒng)進(jìn)行具體的分析。我們先來(lái)思考一個(gè)問(wèn)題,什么是操作系統(tǒng),為什么要有操作系統(tǒng)?我們通過(guò)對(duì)操作系統(tǒng)發(fā)展歷史的研究以及對(duì)Linux內(nèi)核實(shí)現(xiàn)的深入研究發(fā)現(xiàn),操作系統(tǒng)的存在就是為了一個(gè)目的,就是為了運(yùn)行程序,如果再加個(gè)形容詞的話,那就是多快好省地運(yùn)行程序。為了實(shí)現(xiàn)這個(gè)目的,操作系統(tǒng)提供了兩個(gè)作用:1.操作系統(tǒng)是一個(gè)更加高級(jí)的抽象計(jì)算機(jī),2.操作系統(tǒng)是計(jì)算機(jī)資源管理器。下面我們對(duì)一個(gè)目的、兩個(gè)作用展開(kāi)討論。
5.1 操作系統(tǒng)的目的
操作系統(tǒng)存在的目的就是為了運(yùn)行程序。我們還可以用反證法來(lái)論證這個(gè)結(jié)論,假設(shè)操作系統(tǒng)不能用來(lái)運(yùn)行程序,那么操作系統(tǒng)要它還有什么用呢。所以操作系統(tǒng)存在的目的就是為了運(yùn)行程序,為了多快好省地運(yùn)行程序,多就是多任務(wù),快就是效率高,好就是安全穩(wěn)定,省就是節(jié)省資源,為了實(shí)現(xiàn)這個(gè)目的,操作系統(tǒng)做了非常大的努力。
既然是為了運(yùn)行程序,那么我們要問(wèn)的第一個(gè)問(wèn)題就是程序是怎么來(lái)的呢。首先程序是用計(jì)算機(jī)編程語(yǔ)言編寫(xiě)的。編寫(xiě)程序的工具是什么呢,是文本編輯器。編寫(xiě)的原則是什么呢,隨便怎么寫(xiě)都可以嗎,不是的,首先要符合所使用語(yǔ)言的語(yǔ)法才行。其次我們對(duì)程序還有幾個(gè)要求,其中首先最重要的要求就是正確性,程序運(yùn)行結(jié)果不正確可不行,除了編程時(shí)我們小心翼翼盡量保證程序邏輯正確外,我們?cè)谶\(yùn)行的時(shí)候發(fā)現(xiàn)程序不正確時(shí)還要用調(diào)試工具GDB來(lái)調(diào)試程序,找到程序的錯(cuò)誤改正它。對(duì)于程序的內(nèi)存錯(cuò)誤,我們還可以使用asan、malloc-debug、valgrind等工具來(lái)排查錯(cuò)誤。再者我們寫(xiě)的程序還要簡(jiǎn)潔清晰、可讀性強(qiáng)、可維護(hù)性強(qiáng),為了達(dá)到這個(gè)目的,我們要學(xué)習(xí)很多編程的藝術(shù),在此給大家推薦幾本書(shū):《Agile Software Development》《Clean Code》《The Clean Coder》《Clean Architecture》《Code Complete》《Design Patterns》《Refactoring Improving the Design of Existing Code》。其中大家在很多博客上經(jīng)常看到的一些編程原則如開(kāi)閉原則、單一責(zé)任原則、依賴倒置原則等都是出自第一本書(shū),漢譯書(shū)名叫《敏捷軟件開(kāi)發(fā)》。再者我們寫(xiě)的程序還要具有高效性,程序效率低運(yùn)行慢可不行,那么怎么讓我們寫(xiě)的程序比較高效呢,為此我們要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法,只有把數(shù)據(jù)結(jié)構(gòu)與算法學(xué)透了,我們才能把我們學(xué)到的方法和我們的需求結(jié)合起來(lái),寫(xiě)出來(lái)運(yùn)行又快占用空間又小的程序。為此我向大家推薦幾本書(shū):《Introduction to Algorithms》《Algorithm Design and Applications》《Algorithms in a Nutshell》《Data Structures and Algorithms in Java》《Algorithms》《An Introduction to the Analysis of Algorithms》。如果是已經(jīng)寫(xiě)好的程序嫌它運(yùn)行慢應(yīng)該怎么辦呢,我們有性能剖析工具gprof,它能幫你找到熱點(diǎn)代碼,然后你就可以進(jìn)行定向優(yōu)化,必要時(shí)還可以使用匯編語(yǔ)言實(shí)現(xiàn)一些函數(shù)來(lái)提高效率。
當(dāng)我們把源代碼都寫(xiě)好了之后,程序就可以運(yùn)行了嗎,不行,CPU不認(rèn)識(shí)源代碼,只認(rèn)識(shí)二進(jìn)制程序,所以我們還需要把源代碼編譯、鏈接成二進(jìn)制程序才行。二進(jìn)制程序有沒(méi)有格式呢,隨便怎么生成都行嗎,不行,不同的操作系統(tǒng)都有自己特定的二進(jìn)制程序格式。Linux使用的二進(jìn)制程序格式叫做ELF格式,ELF是一個(gè)總體格式,在不同的操作系統(tǒng)上、不同的CPU架構(gòu)上又有一些具體細(xì)化的要求,如都有哪些段,函數(shù)怎么傳遞參數(shù)等,這些統(tǒng)稱為ABI,應(yīng)用程序二進(jìn)制接口。源碼生成的二進(jìn)制程序可以分為exe主程序和so動(dòng)態(tài)庫(kù)程序,主程序可以直接執(zhí)行,庫(kù)程序不能直接執(zhí)行,只能在別人的進(jìn)程里被調(diào)用執(zhí)行。程序運(yùn)行起來(lái)就是進(jìn)程,一個(gè)進(jìn)程由一個(gè)exe主程序和n個(gè)so庫(kù)程序構(gòu)成。
還有一個(gè)概念叫做IDE,集成開(kāi)發(fā)環(huán)境,它是一個(gè)圖形界面程序,里面集成了文件編輯器、調(diào)試器、編譯器、鏈接器、Build工具等,可以在一個(gè)程序里完成一系列開(kāi)發(fā)操作。
5.2 操作系統(tǒng)的作用之一
操作系統(tǒng)為了運(yùn)行程序提供了哪些作用呢,第一個(gè)就是操作系統(tǒng)是一個(gè)更加高級(jí)的抽象計(jì)算機(jī)。怎么理解這句話呢,我們從計(jì)算機(jī)發(fā)展的歷史來(lái)講解。最開(kāi)始的時(shí)候是沒(méi)有操作系統(tǒng)的,只有應(yīng)用程序,應(yīng)用程序的開(kāi)發(fā)要直接面向硬件開(kāi)發(fā),因?yàn)闆](méi)有人給你提供API。比如說(shuō)你要讀寫(xiě)一個(gè)文件,你該怎么辦,要是擱現(xiàn)在那是很簡(jiǎn)單的,你只需要open(file path),read(fd),write(fd)就可以了。但是在當(dāng)時(shí)讀寫(xiě)卻是非常復(fù)雜的,你需要了解磁盤(pán)的型號(hào),知道磁盤(pán)的端口,知道磁盤(pán)的各個(gè)技術(shù)參數(shù)如磁盤(pán)有多少個(gè)盤(pán)面柱面扇區(qū),你要自己編程管理每一個(gè)扇區(qū),記錄哪一個(gè)扇區(qū)用過(guò)了哪一個(gè)扇區(qū)沒(méi)用過(guò),想想都頭疼,都想放棄。現(xiàn)在好了,現(xiàn)在有操作系統(tǒng)幫你做這些事情,你只需要按照操作系統(tǒng)給你的API去做就行了,那是十分的方便啊。操作系統(tǒng)向應(yīng)用程序提供的功能,從底層實(shí)現(xiàn)的角度來(lái)看叫做系統(tǒng)調(diào)用,從上層應(yīng)用的角度來(lái)看叫做API,系統(tǒng)調(diào)用和API看起來(lái)很像又不太一樣,但是它倆又有很強(qiáng)的關(guān)聯(lián)。
5.3 操作系統(tǒng)的作用之二
操作系統(tǒng)的第一個(gè)作用是從單個(gè)進(jìn)程的角度來(lái)看的,操作系統(tǒng)是一個(gè)更加高級(jí)的抽象計(jì)算機(jī)?,F(xiàn)在我們從多個(gè)進(jìn)程的角度來(lái)看,又發(fā)現(xiàn)操作系統(tǒng)其實(shí)是個(gè)計(jì)算機(jī)資源管理器。這和操作系統(tǒng)的標(biāo)準(zhǔn)定義又很契合,我們來(lái)看一下操作系統(tǒng)的標(biāo)準(zhǔn)定義:操作系統(tǒng)是管理計(jì)算機(jī)硬件與軟件資源的計(jì)算機(jī)程序。操作系統(tǒng)需要處理如管理與配置內(nèi)存、決定系統(tǒng)資源供需的優(yōu)先次序、控制輸入設(shè)備與輸出設(shè)備、操作網(wǎng)絡(luò)與管理文件系統(tǒng)等基本事務(wù)。操作系統(tǒng)也提供一個(gè)讓用戶與系統(tǒng)交互的操作界面。
既然操作系統(tǒng)是計(jì)算機(jī)資源管理器,那么我們先來(lái)看一看計(jì)算機(jī)都有哪些資源呢?從第二章我們可以看出,計(jì)算機(jī)最主要的資源有CPU、內(nèi)存、外存、外設(shè),因此我們可以很輕易地看出操作系統(tǒng)的四個(gè)必要組成部分分別是CPU管理、內(nèi)存管理、外存管理、設(shè)備管理。CPU管理就是CPU時(shí)間管理就是進(jìn)程調(diào)度。內(nèi)存管理是對(duì)內(nèi)存資源進(jìn)行管理,包括物理內(nèi)存的管理和虛擬內(nèi)存的管理。外存管理包括文件系統(tǒng)和磁盤(pán)驅(qū)動(dòng)兩部分。設(shè)備管理是最復(fù)雜的,一般都是由內(nèi)核提供基礎(chǔ)代碼和驅(qū)動(dòng)模型,由各個(gè)硬件廠商具體去寫(xiě)自家設(shè)備的驅(qū)動(dòng)程序,系統(tǒng)還會(huì)制定用戶空間接口,提供一些基礎(chǔ)操作庫(kù)。由于Linux支持的設(shè)備眾多,因此設(shè)備驅(qū)動(dòng)代碼是Linux里面代碼最多的部分,占Linux總代碼的比例高達(dá)60%多。
操作系統(tǒng)管理計(jì)算機(jī)資源總體上有四種模式,分別是:1時(shí)間分割管理,2空間分割管理,3獨(dú)占性管理,4直接管理。我們把資源分為共享性資源和非共享性資源。共享不共享是相對(duì)于進(jìn)程來(lái)說(shuō)的,有些資源直接對(duì)整個(gè)系統(tǒng)起作用,對(duì)進(jìn)程不可用,我們把系統(tǒng)對(duì)這種資源的管理方法叫做直接管理,例如中斷控制器。對(duì)于共享性資源,我們要根據(jù)資源自身的特點(diǎn)采取相應(yīng)的管理方式。資源的特點(diǎn)包括時(shí)間分割相似性和空間分割相似性。時(shí)間分割相似性是指在一個(gè)較少的時(shí)間片上資源仍然是可用的??臻g分割相似性是指把資源分割成若干份,每一個(gè)仍然能達(dá)到資源本身的作用。對(duì)于具有時(shí)間分割相似性的資源我們采取時(shí)間分割管理,對(duì)于具有空間分割相似性的資源我們采取空間分割管理,如果兩者都不具有我們采取獨(dú)占性管理。
CPU管理:
對(duì)CPU應(yīng)該怎么管理呢,我們來(lái)思考一下。我們能不能采用空間分割管理呢?試一下,如果用空間分割管理,那么就是把一個(gè)CPU分割為n個(gè)部分,每個(gè)部分都是一個(gè)獨(dú)立的執(zhí)行單元,n個(gè)進(jìn)程各占一個(gè)各運(yùn)行各的,好像也行,但是根據(jù)我們?cè)诘诙滤鶎W(xué)的知識(shí),CPU做不到啊。如果用時(shí)間分割管理呢,把時(shí)間分成一片一片的,輪流分給每個(gè)進(jìn)程來(lái)使用,由于CPU是一條指令一條指令地執(zhí)行,不同進(jìn)程之間的指令是沒(méi)有依賴關(guān)系的,所以是可以的。所以我們對(duì)CPU采取的管理方式是時(shí)間分割管理。對(duì)CPU進(jìn)行時(shí)間分割管理就是進(jìn)程調(diào)度。
進(jìn)程調(diào)度需要統(tǒng)計(jì)進(jìn)程的運(yùn)行時(shí)間,被動(dòng)調(diào)度需要有定時(shí)器的支持,文件系統(tǒng)需要給文件打上時(shí)間戳,用戶空間也有查看時(shí)間、修改時(shí)間、設(shè)置定時(shí)器的需求。
內(nèi)存管理:
我們?cè)賮?lái)看一下對(duì)內(nèi)存應(yīng)該使用什么管理方式。內(nèi)存能不能進(jìn)行時(shí)間分割管理呢?能,把時(shí)間分成一片一片的,每個(gè)時(shí)間片就只有一個(gè)進(jìn)程就能獨(dú)享整個(gè)內(nèi)存。顯然這么做很不利于充分使用內(nèi)存,太浪費(fèi)了,違背了多快好省地運(yùn)行進(jìn)程的原則。那么能不能對(duì)內(nèi)存進(jìn)行空間分割管理呢?能,每個(gè)進(jìn)程占用一小塊內(nèi)存進(jìn)行運(yùn)行,這樣能充分利用整個(gè)內(nèi)存。這么做有沒(méi)有什么缺點(diǎn)呢?有,內(nèi)核和各個(gè)進(jìn)程都在物理內(nèi)存里,大家都可以相互訪問(wèn)甚至破壞別人的內(nèi)存,顯然是不安全的。對(duì)此有什么辦法呢,最先想到的也是最直觀的方法就是分段機(jī)制,修改CPU的運(yùn)行原理,實(shí)現(xiàn)分段機(jī)制,內(nèi)核、每個(gè)進(jìn)程各自運(yùn)行在各自的段里面。由于是在CPU硬件上實(shí)現(xiàn)的限制,誰(shuí)也無(wú)法訪問(wèn)自己段外面的內(nèi)存,這樣就實(shí)現(xiàn)了進(jìn)程隔離,非常安全了。但是這么做有沒(méi)有缺點(diǎn)呢?有,物理內(nèi)存本來(lái)就少,分完段之后物理空間就更小了,而且還有一個(gè)缺點(diǎn)就是程序啟動(dòng)的時(shí)候就要把程序全部加載到內(nèi)存才行,不然運(yùn)行到?jīng)]有加載的程序就崩潰了。對(duì)此有什么解決辦法嗎,最剛開(kāi)始想的辦法是軟件的,每個(gè)程序要自己要寫(xiě)個(gè)overlay manager,程序員要對(duì)自己程序的執(zhí)行流程有清晰的認(rèn)識(shí),自己控制何時(shí)把哪部分即將會(huì)用到的程序加載到內(nèi)存,何時(shí)將哪部分暫時(shí)不會(huì)使用的程序再放回外存,這樣程序就可以使用較少的內(nèi)存也能順利運(yùn)行了。但是這個(gè)做法非常麻煩,對(duì)程序員的考驗(yàn)非常大,要解決這個(gè)問(wèn)題還是得使用硬件機(jī)制才行。于是后來(lái)就產(chǎn)生了虛擬內(nèi)存/分頁(yè)機(jī)制,每個(gè)進(jìn)程都運(yùn)行在虛擬內(nèi)存上,并且獨(dú)占整個(gè)虛擬內(nèi)存空間,這樣就實(shí)現(xiàn)了進(jìn)程隔離,非常安全,同時(shí)進(jìn)程并不立即分配物理內(nèi)存,而是程序在運(yùn)行的過(guò)程中產(chǎn)生page fault 按需調(diào)頁(yè),用到的時(shí)候再去分配物理內(nèi)存,這樣就可以節(jié)省物理內(nèi)存。所以Linux對(duì)內(nèi)存的管理方式是在形式上每個(gè)進(jìn)程都獨(dú)占整個(gè)虛擬內(nèi)存空間,在實(shí)際上進(jìn)程之間對(duì)物理內(nèi)存進(jìn)行空間分割管理,而且是延遲分配,用到的才分配,用不到就不分配,又節(jié)省了物理內(nèi)存,分頁(yè)機(jī)制真是太完美了。
外存管理:
顯然,外存也是采用空間分割管理的。
外設(shè)管理:
有些設(shè)備比如相機(jī),既不具有時(shí)間分割相似性,也不具有空間分割相似性,只能對(duì)其進(jìn)行獨(dú)占性管理。我們可以來(lái)假設(shè)一下,如果對(duì)相機(jī)進(jìn)行時(shí)間分割管理的話,比如說(shuō)A進(jìn)程用了0.5秒拍照拍了一半,B進(jìn)程又用了0.5秒拍照拍了一半,然后A進(jìn)程又用了0.5秒把整個(gè)照片拍完了,接著B(niǎo)進(jìn)程又用0.5秒把整個(gè)照片拍完了,顯然這是不可能的,因?yàn)橄鄼C(jī)做不到。我們?cè)偌僭O(shè)對(duì)相機(jī)進(jìn)行空間分割管理,比如說(shuō)這個(gè)相機(jī)拍照的分辨率是1000×800的,A、B兩個(gè)進(jìn)程同時(shí)用相機(jī),分別拍出了一個(gè)1000×400的照片,這也是不可能的,相機(jī)做不到。所以對(duì)相機(jī)只能采取獨(dú)占性管理,每個(gè)進(jìn)程在使用相機(jī)前都要先申請(qǐng),使用完后再釋放,使用期間進(jìn)程對(duì)相機(jī)是獨(dú)占的。
計(jì)算機(jī)中除了CPU、內(nèi)存、主板、總線等之外的設(shè)備都叫做外設(shè),內(nèi)核對(duì)外設(shè)的管理叫做設(shè)備管理,也是操作系統(tǒng)中非常重要的一塊。外設(shè)的運(yùn)行需要驅(qū)動(dòng)程序,內(nèi)核一般會(huì)提供總線的驅(qū)動(dòng)和一些設(shè)備類型的框架代碼,然后由各個(gè)硬件廠商去編寫(xiě)具體的驅(qū)動(dòng)程序。想學(xué)習(xí)Linux驅(qū)動(dòng)編程的話,推薦閱讀《Linux Device Drivers》《Essential Linux Device Drivers》《Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》。
有些設(shè)備,并不是單單有個(gè)驅(qū)動(dòng)就能獨(dú)立運(yùn)行了,而是與用戶空間庫(kù)和進(jìn)程一起構(gòu)成了一個(gè)新的系統(tǒng),比如網(wǎng)卡和顯示器。網(wǎng)卡需要網(wǎng)卡驅(qū)動(dòng),但是只有網(wǎng)卡驅(qū)動(dòng),網(wǎng)卡還是沒(méi)有用,網(wǎng)卡與網(wǎng)卡驅(qū)動(dòng)還有網(wǎng)絡(luò)協(xié)議還有一些相關(guān)的硬件軟件共同構(gòu)成了網(wǎng)絡(luò)系統(tǒng)。再說(shuō)顯示器,光有個(gè)顯示驅(qū)動(dòng)也是沒(méi)有意義的,顯示器只是計(jì)算機(jī)圖形系統(tǒng)中的一個(gè)組成部分。計(jì)算機(jī)圖形系統(tǒng)還包括GPU、渲染庫(kù)、窗口系統(tǒng)等很多組件。
中斷機(jī)制:
計(jì)算機(jī)中外設(shè)與CPU的交互方式,除了CPU可以控制外設(shè)之外,外設(shè)也可以主動(dòng)向CPU報(bào)告事情,避免了CPU輪詢外設(shè)的低效行為,這種方式就叫做中斷。中斷除了這個(gè)作用之外還有其它一些作用,比如可以用來(lái)處理CPU異常、用來(lái)實(shí)現(xiàn)系統(tǒng)調(diào)用,定時(shí)器中斷還可以用來(lái)實(shí)現(xiàn)搶占式多任務(wù)。所以中斷機(jī)制對(duì)操作系統(tǒng)來(lái)說(shuō)是非常重要的,重要性就相當(dāng)于人的神經(jīng)系統(tǒng)加呼吸系統(tǒng)一樣重要。
編程設(shè)施:
計(jì)算機(jī)除了管理硬件之外,還為軟件提供了兩個(gè)編程設(shè)施,分別是線程同步和進(jìn)程間通信。線程同步是因?yàn)槎鄠€(gè)執(zhí)行流(線程)同時(shí)訪問(wèn)公共數(shù)據(jù)引起的,如果不采取一些措施的話就會(huì)產(chǎn)生程序錯(cuò)誤,因此操作系統(tǒng)提供了線程同步這個(gè)編程設(shè)施來(lái)解決這個(gè)問(wèn)題。線程同步中有一個(gè)非常常用的方法叫做自旋鎖。
進(jìn)程間通信是因?yàn)橛辛颂摂M內(nèi)存/分頁(yè)機(jī)制之后產(chǎn)生了進(jìn)程隔離,進(jìn)程之間無(wú)法直接訪問(wèn)對(duì)方,所以需要通過(guò)內(nèi)核提供的進(jìn)程間通信機(jī)制來(lái)進(jìn)行溝通,具體內(nèi)容請(qǐng)參看《深入理解Linux進(jìn)程間通信》。Linux還有一個(gè)重要的機(jī)制叫做信號(hào)機(jī)制(signal),它和IPC很像,但又不是典型的IPC,也不僅僅是IPC,它還是系統(tǒng)處理CPU異常的方法。
其它模塊:
操作系統(tǒng)還有兩個(gè)重要的模塊:一個(gè)是和社會(huì)因素有關(guān)的安全防護(hù),一個(gè)是和物理因素有關(guān)的電源管理。電源管理包括基本電源管理和高級(jí)電源管理,基本電源管理包括:睡眠、休眠、關(guān)機(jī)、重啟,高級(jí)電源管理也就是各種省電機(jī)制,如CPUIdle、CPUFrequ、DEVFreq,另外溫控也可以算到電源管理里面。
六、計(jì)算機(jī)運(yùn)行模型
學(xué)了上面這些知識(shí)之后,我們心里要有幾幅計(jì)算機(jī)的運(yùn)行模型圖,達(dá)到計(jì)算機(jī)就在我心中運(yùn)行的境界。一是CPU運(yùn)行模型圖,二是進(jìn)程調(diào)度圖,三是總體軟件體系結(jié)構(gòu)圖。
6.1 CPU運(yùn)行模型
最簡(jiǎn)單的CPU模型是圖靈機(jī)模型,非常簡(jiǎn)單,就是一個(gè)一直在線性運(yùn)行的機(jī)器。其次是帶中斷的圖靈機(jī)模型,這個(gè)模型可以在正常的執(zhí)行流之外插入一段額外的執(zhí)行流。然后是具體的CPU運(yùn)行模型,進(jìn)程一般運(yùn)行在用戶空間,借助中斷機(jī)制可以陷入內(nèi)核執(zhí)行一些系統(tǒng)調(diào)用,外設(shè)發(fā)生中斷會(huì)執(zhí)行中斷處理函數(shù)。
實(shí)際的CPU運(yùn)行模型在這個(gè)基礎(chǔ)之上又變得非常復(fù)雜了,如下圖所示。在學(xué)習(xí)了中斷機(jī)制、系統(tǒng)調(diào)用和進(jìn)程調(diào)度之后,對(duì)下面這幾張圖應(yīng)該能理解地比較透徹。
我們?cè)谶@里說(shuō)一下CPU執(zhí)行場(chǎng)景,場(chǎng)景就是英文中的context,一般都翻譯成上下文,我看到有一本書(shū)上翻譯成場(chǎng)景,覺(jué)得這個(gè)翻譯非常信達(dá)雅,所以后面就用場(chǎng)景這個(gè)翻譯了。CPU的執(zhí)行場(chǎng)景一共有兩種:一個(gè)是進(jìn)程執(zhí)行場(chǎng)景,一個(gè)是中斷執(zhí)行場(chǎng)景。進(jìn)程執(zhí)行場(chǎng)景分為兩種情況:用戶空間進(jìn)程執(zhí)行場(chǎng)景和內(nèi)核空間進(jìn)程執(zhí)行場(chǎng)景。進(jìn)程一般在用戶空間運(yùn)行,發(fā)生系統(tǒng)調(diào)用時(shí)會(huì)進(jìn)入到內(nèi)核空間運(yùn)行。中斷執(zhí)行場(chǎng)景只有一種情況,那就是內(nèi)核空間中斷執(zhí)行場(chǎng)景,因?yàn)橹袛嗖荒苓\(yùn)行在用戶空間。中斷執(zhí)行場(chǎng)景和進(jìn)程執(zhí)行場(chǎng)景還有一個(gè)很大的區(qū)別就是進(jìn)程執(zhí)行場(chǎng)景可阻塞可調(diào)度,中斷執(zhí)行場(chǎng)景必須一次性執(zhí)行完,不能阻塞,因?yàn)樗豢烧{(diào)度。
6.2 進(jìn)程調(diào)度模型
在CPU運(yùn)行模型的基礎(chǔ)上,CPU在用戶空間執(zhí)行進(jìn)程,可以通過(guò)系統(tǒng)調(diào)用進(jìn)入內(nèi)核空間,也可能由于中斷或者異常發(fā)生而進(jìn)入內(nèi)核空間,在內(nèi)核空間里進(jìn)程可以由于阻塞而主動(dòng)發(fā)生進(jìn)程調(diào)度,或者在定時(shí)器中斷里面由于時(shí)間片耗盡而發(fā)生調(diào)度。
圖片展示的是UP(單處理器)調(diào)度的情況,每個(gè)時(shí)刻最多只有一個(gè)進(jìn)程會(huì)被選中在CPU上運(yùn)行。如果是SMP(多處理器)的話,就是有NR_CPU個(gè)這樣的圖同時(shí)運(yùn)轉(zhuǎn),互不干擾,這是從運(yùn)行的角度看是這樣的,但是如果從空間的角度看的話,因?yàn)閮?nèi)核空間只有一個(gè),所以圖片應(yīng)該是中心捏合在一起,但是翅膀各是各的那種樣式的圖。
對(duì)于調(diào)度器是如何選擇進(jìn)程進(jìn)行調(diào)度的,我們心中要有下面這張圖。
6.3 軟件體系結(jié)構(gòu)
下面我們?cè)賮?lái)看一下整個(gè)軟件體系放在一起是什么樣的:
我們可以看到內(nèi)核運(yùn)行在硬件之上,進(jìn)程運(yùn)行在內(nèi)核之上,有init等進(jìn)程是作為操作系統(tǒng)的一部分共同維護(hù)整個(gè)系統(tǒng)的運(yùn)行。內(nèi)核里面包括進(jìn)程調(diào)度、內(nèi)存管理、文件系統(tǒng)、BIO層、網(wǎng)絡(luò)模塊、其它驅(qū)動(dòng)等模塊。進(jìn)程是由一個(gè)exe主程序和n個(gè)so庫(kù)程序組成。每個(gè)進(jìn)程中的libc.so,ld.so等庫(kù)程序都是屬于操作系統(tǒng)的一部分。所有進(jìn)程通過(guò)進(jìn)程調(diào)度共享CPU、輪流執(zhí)行。
參考文獻(xiàn):
- 《Operating System Concepts》
- 《Operating Systems Design and Implementation》
- 《Operating Systems Three Easy Pieces》
- 《Operating Systems In Depth》
- 《Operating Systems Internals and Design Principles》
- 《Operating Systems A Concept-Based Approach》
- 《現(xiàn)代操作系統(tǒng)》(陳海波/夏虞斌著)
- 《Windows Internals 7th Part 1》
- 《Windows Internals 7th Part 2》
- 《Mac OS X and iOS Internals》
- 《Mac OS X Internals A Systems Approach》
- 《Linux Kernel Development》
- 《Understanding the Linux Kernel》
- 《Professional Linux Kernel Architecture》
- 《Mastering Linux Kernel Development》
- 《Understanding the Linux Virtual Memory Manager》
- 《Linux內(nèi)核深度解析》
- 《Linux操作系統(tǒng)原理與應(yīng)用》
- 《深度探索Linux操作系統(tǒng)》
- 《ARM Linux內(nèi)核源碼剖析》
- 《奔跑吧Linux內(nèi)核》
- 《Linux內(nèi)核源代碼情景分析》
- 《Linux內(nèi)核設(shè)計(jì)的藝術(shù)》
- 《Linux內(nèi)核完全注釋》
作者簡(jiǎn)介:
程磊,某手機(jī)大廠系統(tǒng)開(kāi)發(fā)工程師,閱碼場(chǎng)榮譽(yù)總編輯,最大的愛(ài)好是鉆研Linux內(nèi)核基本原理。