內存
每個內存單元有一個地址內存地址是從0開始編號的整數,CPU通過地址找到相應的內存單元,取其中的指令或者讀寫其中的數據。一個地址所對應的內存單元只能存一個字節,像int、float等多字節的數據類型保存在內存中要占用連續的多個地址,它們的地址是它所占內存單元的起始地址。
CPU
CPU最核心的功能單元包括:
Register (寄存器)
寄存器是CPU內部的高速存儲器,像內存一樣可以存取數據,但比訪問內存快得多。
一些寄存器可以用在各種運算和讀寫內存的指令中,比如eax寄存器,這稱為通用寄存器(General-purpose Register)。
一些寄存器只能用于某種特定的用途,比如eip用作程序計數器,這稱為特殊寄存器(Special-purpose Register)。
程序計數器是一種特殊寄存器,保存著CPU取下一條指令的地址,CPU按程序計數器保存的地址去內存中取指令然后解釋執行,這時程序計數器保存的地址會自動加上該指令的長度,指向內存中的下一條指令。
Instruction Decoder (指令譯碼器)
CPU取上來的指令由若干個字節組成,這些字節中有些位表示內存地址,有些位表示寄存器編號,有些位表示這種指令做什么操作,是加減乘除還是讀寫內存,指令譯碼器負責解釋這條指令的含義,然后調動相應的執行單元去執行它。
ALU (Arithmetic and Logic Unit,算術邏輯單元)
如果譯碼器將一條指令解釋為運算指令,就調動算術邏輯單元去做運算,比如加減乘除、位運算、邏輯運算。指令中會指示運算結果保存到哪里,可能保存到寄存器中,也可能保存到內存中。
Bus (地址和數據總線)。
CPU和內存之間用地址總線、數據總線和控制線連接起來,每條線上有1和0兩種狀態。
從CPU訪問內存
如果在執行指令過程中需要訪問內存,比如從內存讀一個數到寄存器,執行過程可以想像成這樣:
1.CPU內部將寄存器對接到數據總線上,使寄存器的每一位對接到一條數據線,等待接收數據。
2.CPU通過控制線發一個讀請求,并且將內存地址通過地址線發給內存。
3.內存收到地址和讀請求之后,將相應的內存單元對接到數據總線的另一端,這樣,內存單元每一位的1或0狀態通過一條數據線到達CPU寄存器中相應的位,就完成了數據傳送。
上圖中畫了32條地址線和32條數據線,CPU寄存器也是32位,可以說這種體系結構是32位的,比如x86就是這樣的體系結構,目前主流的處理器是32位或64位的。地址線、數據線和CPU寄存器的位數通常是一致的,從上圖可以看出數據線和CPU寄存器的位數應該一致,另外有些寄存器(比如程序計數器)需要保存一個內存地址,因而地址線和CPU寄存器的位數也應該一致。32位計算機有32條地址線,地址空間(Address Space)從0x00000000到0xffffffff,共4GB,而64位計算機有更大的地址空間。
這里的的地址線、數據線是指CPU的內總線,是直接和CPU的執行單元相連的,內總線經過MMU和總線接口的轉換之后引出到芯片引腳才是外總線,外地址線和外數據線的位數都有可能和內總線不同,例如32位處理器的外地址總線可尋址的空間可以大于4GB。
CPU從內存中取值
1.eip寄存器指向地址0x80483a2,CPU從這里開始取一條5個字節的指令,然后eip寄存器指向下一條指令的起始地址0x80483a7。
2.CPU對這5個字節譯碼,得知這條指令要求從地址0x804a01c開始取4個字節保存到eax寄存器。
3.執行指令,讀內存,取上來的數是3,保存到eax寄存器。注意,地址0x804a01c~0x804a01f里存儲的四個字節不能按地址從低到高的順序看成0x03000000,而要按地址從高到低的順序看成0x00000003。也就是說,對于多字節的整數類型,低地址保存的是整數的低位,這稱為小端(Little Endian)字節序(Byte Order)。x86平臺是小端字節序的,而另外一些平臺規定低地址保存整數的高位,稱為大端(Big Endian)字節序。
4.CPU從eip寄存器指向的地址取一條3個字節的指令,然后eip寄存器指向下一條指令的起始地址0x80483aa。
5.CPU對這3個字節譯碼,得知這條指令要求把eax寄存器的值加1,結果仍保存到eax寄存器。
6.執行指令,現在eax寄存器中的數是4。
7.CPU從eip寄存器指向的地址取一條5個字節的指令,然后eip寄存器指向下一條指令的起始地址0x80483af。
8.CPU對這5個字節譯碼,得知這條指令要求把eax寄存器的值保存到從地址0x804a018開始的4個字節。
9.執行指令,把4這個值保存到從地址0x804a018開始的4個字節(按小端字節序保存)。
設備
CPU訪問設備的兩種方式:
Memory-mapped I/O (內存映射I/O)
圖中①②訪問這種設備就像訪問內存一樣(略有不同,如下表),按地址讀寫即可。比如ARM。
Port I/O (端口I/O)
圖中③需要用一種專用的in/out指令訪問。比如x86。
第三種方式:設備總線
由于計算機的設備五花八門,于是出現了各種適應不同要求的設備總線,設備總線的控制器也是一種設備。CPU通過內存映射I/O或端口I/O訪問相應的總線控制器,通過總線控制器再去訪問掛在總線上的設備。
比如PCI、AGP、USB、1394、SATA等等,這些設備是掛在設備總線上的。
上圖中標有“設備”的框都可能是實際的設備,也可能是設備總線的控制器。
Interrupt中斷機制
每個設備都有一條中斷線,通過中斷控制器連接到CPU,當設備需要主動通知CPU時就引發一個中斷信號,CPU正在執行的指令將被打斷,程序計數器會指向某個固定的地址(這個地址由體系結構定義),于是CPU從這個地址開始取指令(或者說跳轉到這個地址),執行中斷服務程序(ISR,Interrupt Service Routine),完成中斷處理之后再返回先前被打斷的地方執行后續指令。
比如某種體系結構規定發生中斷時跳轉到地址0x00000010執行,那么就要事先把一段ISR程序加載到這個地址,ISR程序是內核代碼的一部分,在這段代碼中首先判斷是哪個設備引發了中斷,然后調用該設備的中斷處理函數做進一步處理。
設備寄存器
設備中可供讀寫訪問的單元通常稱為設備寄存器(注意和CPU寄存器不是一回事)。
操作設備的過程就是讀寫這些設備寄存器的過程,比如向串口發送寄存器里寫數據,串口設備就會把數據發送出去,讀串口接收寄存器的值,就可以讀取串口設備接收到的數據。
硬盤
在x86平臺上,硬盤是掛在IDE、SATA或SCSI總線上的設備,保存在硬盤上的程序是不能被CPU直接取指令執行的,操作系統在執行程序時會把它從硬盤拷貝到內存,這樣CPU才能取指令執行。
操作系統
Operating System(操作系統)本身也是一段保存在磁盤上的程序,計算機在啟動時執行一段固定的啟動代碼(稱為Bootloader)首先把操作系統從磁盤加載到內存,然后執行操作系統中的代碼把用戶需要的其它程序加載到內存。
操作系統最核心的功能是管理進程調度、管理內存的分配使用和管理各種設備,做這些工作的程序稱為Kernel(內核),在我的系統上內核程序是/boot/vmlinuz-2.6.28-13-generic文件,它在計算機啟動時加載到內存并常駐內存。
廣義上操作系統的概念還包括一些必不可少的用戶程序,比如Shell是每個Linux系統必不可少的,而Office辦公套件則是可有可無的,所以前者也屬于廣義上操作系統的范疇,而后者屬于應用軟件。
設備驅動程序
由于各種設備的操作方法各不相同,每種設備都需要專門的Device Driver(設備驅動程序),一個操作系統為了支持廣泛的設備就需要有大量的設備驅動程序,事實上Linux內核源代碼中絕大部分是設備驅動程序。設備驅動程序通常是內核里的一組函數,通過讀寫設備寄存器實現對設備的初始化、讀、寫等操作,有些設備還要提供一個中斷處理函數供ISR調用。
MMU
現代操作系統普遍采用Virtual Memory Management(虛擬內存管理)機制,這需要處理器中的MMU(Memory Management Unit,內存管理單元)提供支持。
PA物理地址
如果處理器沒有MMU,或者有MMU但沒有啟用,CPU執行單元發出的內存地址將直接傳到芯片引腳上,被內存芯片(以下稱為物理內存,以便與虛擬內存區分)接收,這稱為PA(Physical Address,物理地址)。
VA虛擬地址
如果處理器啟用了MMU,CPU執行單元發出的內存地址將被MMU截獲,從CPU到MMU的地址稱為VA(Virtual Address,虛擬地址),而MMU將這個地址翻譯成另一個地址發到CPU芯片的外部地址引腳上,也就是將VA映射成PA
如果是32位處理器,則內地址總線是32位的,與CPU執行單元相連(右圖只是示意性地畫了4條地址線),而經過MMU轉換之后的外地址總線則不一定是32位的。也就是說,虛擬地址空間和物理地址空間是獨立的,32位處理器的虛擬地址空間是4GB,而物理地址空間既可以大于也可以小于4GB。
MMU將VA映射到PA是以Page(頁)為單位的,32位處理器的頁尺寸通常是4KB。例如,MMU可以通過一個映射項將VA的一頁0xb70010000xb7001fff映射到PA的一頁0x20000x2fff,如果CPU執行單元要訪問虛擬地址0xb7001008,則實際訪問到的物理地址是0x2008。物理內存中的頁稱為Page Frame(物理頁面或者頁幀)。
虛擬內存的哪個頁面映射到物理內存的哪個頁幀是通過Page Table(頁表)來描述的,頁表保存在物理內存中,MMU會查找頁表來確定一個VA應該映射到什么PA。
操作系統和MMU是這樣配合的:
1.操作系統在初始化或分配、釋放內存時會執行一些指令在物理內存中填寫頁表,然后用指令設置MMU,告訴MMU頁表在物理內存中的什么位置。
2.設置好之后,CPU每次執行訪問內存的指令都會自動引發MMU做查表和地址轉換操作,地址轉換操作由硬件自動完成,不需要用指令控制MMU去做。
我們在程序中使用的變量和函數都有各自的地址,程序被編譯后,這些地址就成了指令中的地址,指令中的地址被CPU解釋執行,就成了CPU執行單元發出的內存地址,所以在啟用MMU的情況下,程序中使用的地址都是虛擬地址,都會引發MMU做查表和地址轉換操作。
MMU除了做地址轉換之外,還提供內存保護機制。 各種體系結構都有User Mode(用戶模式)和Privileged Mode(特權模式)之分,操作系統可以在頁表中設置每個內存頁面的訪問權限,有些頁面不允許訪問,有些頁面只有在CPU處于特權模式時才允許訪問,有些頁面在用戶模式和特權模式都可以訪問,訪問權限又分為可讀、可寫和可執行三種。這樣設定好之后,當CPU要訪問一個VA時,MMU會檢查CPU當前處于用戶模式還是特權模式,訪問內存的目的是讀數據、寫數據還是取指令,如果和操作系統設定的頁面權限相符,就允許訪問,把它轉換成PA,否則不允許訪問,產生一個Exception(異常)。
異常的處理過程和中斷類似,不同的是中斷由外部設備產生而異常由CPU內部產生,中斷產生的原因和CPU當前執行的指令無關,而異常的產生就是由于CPU當前執行的指令出了問題,例如訪問內存的指令被MMU檢查出權限錯誤,除法指令的除數為0等都會產生異常。
通常操作系統把虛擬地址空間劃分為用戶空間和內核空間,例如x86平臺的Linux系統虛擬地址空間是0x000000000xffffffff,前3GB(0x000000000xbfffffff)是用戶空間,后1GB(0xc0000000~0xffffffff)是內核空間。用戶程序加載到用戶空間,在用戶模式下執行,不能訪問內核中的數據,也不能跳轉到內核代碼中執行。這樣可以保護內核,如果一個進程訪問了非法地址,頂多這一個進程崩潰,而不會影響到內核和整個系統的穩定性。CPU在產生中斷或異常時不僅會跳轉到中斷或異常服務程序,還會自動切換模式,從用戶模式切換到特權模式,因此從中斷或異常服務程序可以跳轉到內核代碼中執行。事實上,整個內核就是由各種中斷和異常處理程序組成的。總結一下:在正常情況下處理器在用戶模式執行用戶程序,在中斷或異常情況下處理器切換到特權模式執行內核程序,處理完中斷或異常之后再返回用戶模式繼續執行用戶程序。
Segmentation fault (段錯誤)的產生過程:
1.用戶程序要訪問的一個VA,經MMU檢查無權訪問。
2.MMU產生一個異常,CPU從用戶模式切換到特權模式,跳轉到內核代碼中執行異常服務程序。
3.內核把這個異常解釋為段錯誤,把引發異常的進程終止掉。
存儲器
Memory Hierarchy
-
處理器
+關注
關注
68文章
19372瀏覽量
230434 -
寄存器
+關注
關注
31文章
5358瀏覽量
120775 -
計算機
+關注
關注
19文章
7522瀏覽量
88296 -
ISR
+關注
關注
0文章
38瀏覽量
14454 -
中斷控制器
+關注
關注
0文章
59瀏覽量
9475
發布評論請先 登錄
相關推薦
評論