計(jì)算機(jī)的 CPU、操作系統(tǒng)與內(nèi)存管理
本文轉(zhuǎn)載自微信公眾號(hào)「神光的編程秘籍」,作者神說要有光zxg。轉(zhuǎn)載本文請(qǐng)聯(lián)系神光的編程秘籍公眾號(hào)。
一些關(guān)于計(jì)算機(jī)原理的思考,大家隨意看看就行。
硬布線到馮諾伊曼
其實(shí)最早的計(jì)算機(jī)是沒有存儲(chǔ)機(jī)制的,都是通過硬布線來編程,編程就是布線,后來的電子計(jì)算機(jī)有了存儲(chǔ)的機(jī)制,能夠存儲(chǔ)一些指令和數(shù)據(jù),這樣就有了能夠復(fù)用的程序的存在基礎(chǔ),通過電和磁來存儲(chǔ)二進(jìn)制信息,替代掉了硬布線。
計(jì)算機(jī)之所以能存儲(chǔ)是因?yàn)樗菆D靈機(jī)模型的實(shí)現(xiàn),存儲(chǔ)的部分對(duì)應(yīng)了圖靈機(jī)模型中的紙帶,馮諾伊曼體系結(jié)構(gòu)(現(xiàn)代計(jì)算機(jī)的體系結(jié)構(gòu))是圖靈機(jī)的實(shí)現(xiàn),它設(shè)計(jì)了用二進(jìn)制表示機(jī)器碼、存儲(chǔ)以及計(jì)算機(jī)五大部件:輸入、輸出、控制、運(yùn)算、存儲(chǔ)。
現(xiàn)在的計(jì)算機(jī)有 cpu 內(nèi)的寄存器和 L1、L2、L3 緩存 ,還有內(nèi)存和硬盤的存儲(chǔ)方式,這 6 級(jí)存儲(chǔ)介質(zhì)就是計(jì)算機(jī)的存儲(chǔ)體系。計(jì)算機(jī)發(fā)展到現(xiàn)在的存儲(chǔ)體系,存儲(chǔ)已經(jīng)是一個(gè)常識(shí)了。存儲(chǔ)是程序復(fù)用的基礎(chǔ),可以重復(fù)執(zhí)行一些編寫好的代碼。而且編譯的出現(xiàn)和發(fā)展,使得我們不用輸入機(jī)器碼,可以用字符構(gòu)成的高級(jí)語言編程,然后編譯為二進(jìn)制的機(jī)器語言。
cpu 與時(shí)鐘和 jump
程序最終是 cpu 執(zhí)行的,而 cpu 執(zhí)行什么指令是由 CS + IP 寄存器控制,cpu 會(huì)不斷的取這倆寄存器所表示的內(nèi)存地址的指令,通過數(shù)據(jù)總線讀入 cpu,用譯碼器譯碼之后用不同電路執(zhí)行。取指令、譯碼、執(zhí)行指令,這是 cpu 的一個(gè)始終周期,cpu 會(huì)無限循環(huán)這個(gè)過程,指令周期的執(zhí)行頻率受時(shí)鐘控制,有一定的頻率,有的計(jì)算機(jī)可以支持短時(shí)間的超頻,就是提高了這個(gè)時(shí)鐘的頻率。cpu 是不斷循環(huán)時(shí)鐘周期的,那么總要有個(gè)初始地址,這個(gè)地址一般是固定的地址,放計(jì)算機(jī)通電以后加載的第一條指令,這部分代碼叫做 BIOS(基礎(chǔ)輸入輸出系統(tǒng)),它是后續(xù)一切程序執(zhí)行的基礎(chǔ)。指令支持跳轉(zhuǎn)到另一個(gè)地址,通過這種方式支持了控制流。就像前面所說,cpu 是不斷循環(huán)時(shí)鐘周期的,那么總要有種方式來控制它轉(zhuǎn)向,控制轉(zhuǎn)向的功能就是通過跳轉(zhuǎn)指令, 所以說 jump 指令是 cpu 的方向盤。而指令和數(shù)據(jù)可以放在內(nèi)存的任何位置,通過 jump 的方向盤控制 cpu 跑到那里。
cpu 是一輛汽車,那么時(shí)鐘就是引擎,控制著 cpu 執(zhí)行指令的頻率,而執(zhí)行到哪條指令是存在 CS + IP 寄存器里的,它只會(huì)往前開,也就是只會(huì)不斷的取下一條指令,而轉(zhuǎn)向(也就是控制流)是由 jump 指令控制的,跑到內(nèi)存中的任何地方取指令來到 cpu 上跑,而最開始的一段路就是 BIOS 程序所在的地方。
BIOS、裸機(jī)和操作系統(tǒng)
BIOS 程序其實(shí)一般不會(huì)自己寫,除非做嵌入式,直接在裸機(jī)(沒有操作系統(tǒng)的計(jì)算機(jī))上開發(fā),那需要自己做程序的引導(dǎo),自己可以控制把指令存在哪,數(shù)據(jù)存在哪,從哪里取。這種邏輯太過通用,而且只有一個(gè)控制流,只有一條路線和風(fēng)景,不能充分利用 cpu ,為了解決這個(gè)問題,出現(xiàn)了操作系統(tǒng),它能多到多個(gè)控制流,也就是汽車有多條行進(jìn)路線,可以看到不同的風(fēng)景。
操作系統(tǒng)也有自己的 BIOS 程序,是 cpu 最開始的一段路,把 cpu 帶到操作系統(tǒng)所在的地方,操作系統(tǒng)一般都提供了進(jìn)程的機(jī)制,也把內(nèi)存管理了起來,不再是隨意的亂用,而是需要需要申請(qǐng),就像我們企業(yè)內(nèi)會(huì)把管理起來的東西做審批流一樣,申請(qǐng)之后才會(huì)分配內(nèi)存,進(jìn)程的程序就放在這塊內(nèi)存上。邏輯是一條控制流,進(jìn)程則是可以有多條控制流,雖然一輛汽車(一個(gè) cpu)只能同時(shí)跑一個(gè)地方,但是卻可以在其它代碼不需要它的時(shí)候去給別的進(jìn)程跑跑,就像順風(fēng)車一樣不用的時(shí)候帶帶別人一樣。cpu 不是一直在跑么,為啥會(huì)有不用它的時(shí)候,因?yàn)榇疟P或者網(wǎng)卡等設(shè)備都有自己的控制器,叫做 DMA,當(dāng)跑到那個(gè)地方的時(shí)候,會(huì)讓 DMA 去搬運(yùn)東西,就像叫了貨拉拉但自己搬東西一樣。當(dāng) DMA 搬運(yùn)設(shè)備數(shù)據(jù)到內(nèi)存的過程中,cpu 需要等,就閑下來了。這時(shí)候不如去跑跑別的代碼,當(dāng)然也可以啥也不干就等在那(阻塞的等待)。cpu 是可以在 io 的時(shí)候跑別的代碼,跑的路線叫做控制流,控制流也有兩種類型,一種是進(jìn)程、一種是線程,線程可以訪問進(jìn)程分配的內(nèi)存等資源,而不同進(jìn)程有各自的分配的資源,不能互相訪問,需要用其他內(nèi)存來做中轉(zhuǎn)。
我們知道,裸機(jī)時(shí)程序可以訪問和操作所有內(nèi)存,由程序自己控制,但是有了操作系統(tǒng),就要遵循操作系統(tǒng)的規(guī)范了,操作系統(tǒng)對(duì)于可執(zhí)行文件有格式的要求,只有一定的格式才能作為機(jī)器碼來執(zhí)行,windows 上是 exe 格式,linux 等系統(tǒng)上是 elf 格式。elf 格式會(huì)把文件分為代碼段 (text)、數(shù)據(jù)段(data)等不同的部分,來放不同的數(shù)據(jù),這樣文件加載到內(nèi)存中會(huì)放在不同的地方,然后會(huì)從 text 段取代碼執(zhí)行,從 data 段取全局?jǐn)?shù)據(jù),運(yùn)行時(shí)還有調(diào)用棧和堆所占用的內(nèi)存。不同進(jìn)程在各自的內(nèi)存上加載自己的可執(zhí)行文件,然后在執(zhí)行時(shí)會(huì)形成不同的控制流,會(huì)在棧和堆上放不同的數(shù)據(jù)。這是受操作系統(tǒng)限制的內(nèi)存管理,由操作系統(tǒng)決定,相比裸機(jī)少了一些自由(必須做成可執(zhí)行文件的格式),但多了各種操作系統(tǒng)提供的能力(系統(tǒng)調(diào)用和標(biāo)準(zhǔn)庫)。
操作系統(tǒng)和 vm 的內(nèi)存管理
操作系統(tǒng)給可執(zhí)行文件跑起來的進(jìn)程分配了內(nèi)存,然后這個(gè)進(jìn)程可以內(nèi)部再做劃分,比如 vm,會(huì)在自己的內(nèi)存上再做一次內(nèi)存管理,各種解釋型語言就是跑在這塊內(nèi)存上,因?yàn)樽约汗芾?,就不用不同操作系統(tǒng)有不同的內(nèi)存格式了,可以統(tǒng)一成一種(能跨平臺(tái))。比如 jvm 有方法區(qū)、靜態(tài)域、堆、棧等等, 而 js vm 有全局作用域(類似靜態(tài)域)、調(diào)用棧(函數(shù)上下文的棧)、堆等。這些 vm 因?yàn)樽约河肿隽艘粚觾?nèi)存管理,所以上面跑的字節(jié)碼等受到的內(nèi)存管理方式不是操作系統(tǒng)的,而是 vm 自己設(shè)計(jì)的。但是不管怎么設(shè)計(jì),總還是要存放代碼、數(shù)據(jù)的,動(dòng)態(tài)分配基本都是在堆上,全局?jǐn)?shù)據(jù)放靜態(tài)域,有調(diào)用棧維護(hù)上下文,這個(gè)基本是通用的設(shè)計(jì)了。
內(nèi)存管理主要是管理代碼和數(shù)據(jù)在內(nèi)存某個(gè)位置的寫入和修改,通過變量的方式引用某塊內(nèi)存,不同的變量類型引用不同大小的內(nèi)存。從裸機(jī)上的隨意修改內(nèi)存,到操作系統(tǒng)上進(jìn)程受限制的操作內(nèi)存,再到 vm 上對(duì)內(nèi)存做的進(jìn)一步設(shè)計(jì)。不同層次都會(huì)做自己的設(shè)計(jì),但是總歸是交給 cpu 來跑的,當(dāng) cpu 跑到這塊內(nèi)存,用 jump 來控制轉(zhuǎn)向,同時(shí)利用操作系統(tǒng)提供的線程能力,可以跑不同的路線(控制流)。
總結(jié)
本文主要是梳理了一些計(jì)算機(jī)的歷史和原理,從硬布線到馮諾伊曼體系,到 6 級(jí)的存儲(chǔ)體系的發(fā)展過程是發(fā)展歷史的部分,而 cpu 的指令周期與時(shí)鐘頻率是 cpu 的工作方式,cpu 不斷執(zhí)行指令周期,跳到別的地方(改變控制流)是通過 jump 指令實(shí)現(xiàn)的,上層高級(jí)語言基于 jump 封裝出了 if、else、函數(shù)調(diào)用等。
計(jì)算機(jī)最開始執(zhí)行的引導(dǎo)程序是 BIOS,裸機(jī)和操作系統(tǒng)都需要,操作系統(tǒng)提供了進(jìn)程管理的能力,可能同時(shí)有多個(gè)控制流,而且提供了各種軟硬件管理能力,暴露了系統(tǒng)調(diào)用和標(biāo)準(zhǔn)庫。操作系統(tǒng)可以加載一定格式的可執(zhí)行文件,不同系統(tǒng)有自己的進(jìn)程的內(nèi)存管理方式,而 vm 則是在操作系統(tǒng)分配的內(nèi)存上自己做了一層內(nèi)存管理,現(xiàn)在很多跨平臺(tái)軟件都是基于這種跑在 vm 上的語言的。雖然不同 vm 的內(nèi)存管理的設(shè)計(jì)有不同,但堆棧等設(shè)計(jì)確是通用的。
注:雖然我們做前端開發(fā)不需要了解太多計(jì)算機(jī)原理,但是理解計(jì)算機(jī)是提升內(nèi)功,內(nèi)功提升了,做任何開發(fā)都會(huì)有更深入的認(rèn)識(shí)。這篇是隨便寫的一些想法,希望能拋磚引玉,引起大家對(duì)計(jì)算機(jī)原理的一些思考。