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