第一章 對於程式設計師來說CPU是什麼
1.1 CPU內部結構解析
程式是計算機進行每一步動作的一組指令,程式由指令+資料組成。
機器語言是CPU可以直接識別並且能夠直接使用的語言,由二進位制程式碼0和1組成。
正在執行的程式被儲存在記憶體中,表示命令和資料儲存位置的數值。
CPU是由運算器(運算從記憶體讀入暫存器的值)、控制器(把記憶體中的指令和資料讀入暫存器,並根據指令的結果控制整個計算機)、時鐘(負責CPU開始計時的時鐘訊號--單位赫茲,Hz,頻率越高,CPU執行速度越快)和暫存器(暫存指令、資料等處理物件)這四部分組成。
記憶體負責儲存指令+資料,DRAM(dynamic random access memory)動態隨機儲存器。其特點是執行速度快,斷電資料丟失。
程式的執行機制:程式執行後,根據時鐘訊號,控制器會將記憶體中的指令和資料讀入到暫存器中,運算器負責運算這些讀入到暫存器中的資料,控制器根據運算器所得的結果進而控制計算機。
1.2 CPU是暫存器的集合體
我們通過1.1中對程式的執行機制的瞭解,我們會發現程式的執行過程其實就是和暫存器不斷打交道的過程,因此也就不難理解CPU就是暫存器的集合體這句話了。我們根據暫存器的不同功能,大致可以將暫存器分為八大類別:CPU內只有1個的-->累加暫存器、標指暫存器、程式計數器、指令暫存器、棧暫存器,有多個的-->基址暫存器、變址暫存器、通用暫存器。此處大家只需瞭解暫存器大致分為這八類即可,對於這些暫存器的具體作用等到後邊具體使用的時候我在詳細介紹哈。
上圖中白色字體表示CPU中該暫存器只有一個,藍色字型代表該暫存器有多個。
1.3 決定程式流程的程式計數器
CPU的控制器會參照程式計數器的數值,從相應的記憶體中讀取指令和資料並執行與之對應的程式。
1.4 順序執行和迴圈機制
順序執行時程式計數器每執行一次自動+1,跳轉指令時則執行jump指令跳轉到指定的地址。
1.5函式的呼叫機制
在講解函式呼叫機制之前,我們先來比較一下 跳轉指令&函式呼叫的不同之處
表面上,兩者的實現方式貌似相似——都是根據程式計數器設定的值跳轉到指定的地址處執行程式,但是在記憶體中的實際執行機制上邊卻又是完全不同,一下舉兩個簡單的小栗子哈
跳轉指令:
1 int main()
2 {
3 int a = -5;
4 //下一條指令的地址
5 if (a >= 0)
6 {
7 printf("%d\n",a);
8 }
9 else
10 {
11 a = -a;
12 printf("%d\n",a);
13 }
14 return 0;
15 }
我們不難看出,程式執行的順序是1--2--3--4--5--11--12--14,在執行跳轉指令之前,如果第四行有程式則會先去執行第四行的程式。
而函式執行卻與此不同
1 #include <stdio.h>
2 int add(int a,int b)
3 {
4 return a + b;
5 }
6 int main()
7 {
8 int a = 10;
9 int b = 20;
10 int c = add(a,b);
11 printf("%d\n", c);
12 return 0;
13 }
由於main()函式是程式的入口,因此該程式的執行順序是7-->8-->9-->10-->4-->5-->10-->11-->12-->13
細心的你或許就會發現,跳轉指令和函式呼叫還是有區別的。事實上,在記憶體中,跳轉指令執行的是jump指令,它所要關心的只是所要跳轉的地址,而不關心返回的問題。但是函式呼叫就不一樣了,它不僅要關注所要呼叫的地址,還要有返回的問題,其實,在記憶體中函式呼叫執行的是call指令,call指令會先把呼叫結束後要執行的下一條指令的地址儲存在記憶體的棧裡邊去,然後根據棧先進後出的特點,函式在呼叫完畢之後便會從記憶體中釋放掉,也就是所謂的出棧,此時便可以通過函式的出口執行return指令,該指令的作用就是將下一條指令的地址的值設定到程式計數器中。
1.6 通過地址和索引實現陣列
在此之前介紹兩個暫存器——基址暫存器和變址暫存器,以及進位制的表示方法。
基址暫存器:儲存資料記憶體的起始地址;
變址暫存器:儲存基址暫存器的相對地址。
進位制的表示方法:4位二進位制可以表示的範圍是 0000~1111 ,轉化成十進位制所表示的範圍就是 0~16 (0 1 2 3 4 5 6 7 8 9 10 A B C D E F),其中A B C D E F這幾個字母不區分大小寫表示的數值是11 、12、13、14、15、16,因此1個16進位制的數由4個2進位制數表示。那麼,在32位機器上便能夠表示的記憶體地址就是00000000000000000000000000000000 ~ 11111111111111111111111111111111,此處為了方便沃恩用十六進位制表示:0000 0000 ~ FFFF FFFF。則可以通過這種方法模擬出陣列的操作:將1000 0000存入基址暫存器(固定值),將 0000 0000 ~ 0000 FFFF之間的值存入到變址暫存器中,那麼基址暫存器+變址暫存器的值便是訪問記憶體的實際地址。
理解了這樣的儲存方式,也就不難理解為什麼我們在訪問陣列元素的時候第一個元素的下標是0了。
所以,CPU的處理並不是特別的複雜,機器語言的主要型別大致可以分為四類:
- 資料傳送指令
- 運算指令
- 跳轉指令
- call / return 指令
以上便是第一章的讀書筆記和心得體會,由於筆者水平有限,篇幅中難免有紕漏之處,也歡迎各位讀者批評指正。