cxuan自己的 Github 非常硬核,求各位大佬 star: https://github.com/crisxuan/bestJavaer
匯編代碼是計算機的一種低級表示,它是一種低級語言,可以從字面角度去理解它,包括處理數據、管理內存、讀寫存儲設備上的數據,以及利用網絡通信等。編譯器生成機器碼經過了一系列的轉換,這些轉換遵循編程語言
、目標機器的指令集
和操作系統
。
指令集
指令集就是指揮計算機工作的指令,因為程序就是按照一定執行順序排列的指令。因為計算機的執行控制權由 CPU 操作,所以指令集就是 CPU 中用來計算和控制計算機的一系列指令的集合。每個 CPU 在產出時都規定了與硬件電路相互配合工作的指令集。
指令集有不少分類,但是一般分為兩種,一種是精簡指令集
,一種是復雜指令集
。具體描述如下
精簡指令集
精簡指令的英文是 reduced instruction set computer, RISC
,原意是精簡指令集計算,簡稱為精簡指令集,是 CPU 的一種 設計模式
,可以把 CPU 想象成一家流水線工廠,對指令數目
和尋址方式
都做了精簡,使其實現更容易,指令並行執行程度更好,編譯器的效率更高。
常見的精簡指令集處理器包括 ARM、AVR、MIPS、PARISC、RISC-V 和 SPARC。
所以你就能理解

這本書是講啥的了。
它主要是基於 MIPS 體系結構把馮諾依曼體系的五大組件進行了逐一的硬件實現 + 軟件設計介紹,更為重要的是引入了諸多並行計算的內容,這是大部分教材中忽略或者內容較少的,會根據這個思路把並行相關的內容,結合 OpenMP, CUDA 和 Hadoop/Spark 整體融入到新書中,畢竟這是未來發展的趨勢
還有這本書

這本書又是講啥的。
這本書是講 RISC-V 指令集的,因為指令集的不同也區分了三個版本,三個版本???嗯,還有下面這個

這本書是講 ARM 指令集的。
所以一般在看 CASPP 的時候並發的看看這本書是非常不錯的選擇。
精簡指令集一般具有如下特征
- 統一的指令編碼
- 通用的寄存器,一般會區分整數和浮點數
- 簡單的尋址模式,復雜尋址模式被簡單指令序列來取代
- 支持很少偏門的類型,例如 RISC 支持字節字符串類型。
復雜指令集
復雜指令集的英文是 Complex Instruction Set Computing, CISC
,是一種微處理器指令集架構,也被譯為復雜指令集。
復雜指令集包括 System/360、VAX、x86 等。
復雜指令集可以說是在精簡指令集之上作出的改變。
復雜指令集的特點是指令數目多而復雜,每條指令字長並不相等,計算機必須加以判讀,並為此付出了性能的代價。
一般來說,提升 CPU 性能的方法有如下這幾種
- 增加寄存器的大小
- 增進內部的並行性
- 增加高速緩存的大小
- 增加核心時脈的速度
- 加入其他功能,例如 IO 和計時器
- 加入向量處理器
- 硬件多線程技術
比較抽象,我們后面會組織成文章具體介紹一下。
C 編譯器會接收其他操作並把其轉換為匯編語言
輸出,匯編語言是機器級別的代碼表示。我們之前介紹過,C 語言程序的執行過程分為下面這幾步

下面我們更多的討論都是基於匯編代碼來討論。
我們日常所接觸的高級語言,都是經過了層層封裝的結果,所以我們平常是接觸不到匯編語言的,更不會用匯編語言來進行編程,這就和你不知道操作系統的存在一樣,但其實你每個操作,甚至你雙擊一個圖標都和操作系統有關系。
高級語言的抽象級別很高,但是經過了層層抽象之后,高級語言的執行效率肯定沒有匯編語言高,也沒有匯編語言可靠。
但是高級語言有更大的優點是其編譯后能夠在不同的機器上運行,匯編語言針對不同的指令集有不同的表示。並且高級語言學習來更加通俗易懂,降低計算機門檻,讓內卷更加嚴重(當然這是開個玩笑,冒犯到請別當真)。
話不多說,了解底層必須了解匯編語言。否則一個 synchronized 底層實現就能夠讓你頭疼不已。而且,天天飄着也不好,遲早要落地。
了解匯編代碼也有助於我們優化程序代碼,分析代碼中隱含的低效率,並且這種優化方法一旦優化成功,將是量級的提高,而不是改改 if...else ,使用一個新特性所能比的。
機器級代碼
計算機系統使用了多種不同形式的抽象,可以通過一個簡單的抽象模型來隱藏實現細節。對於機器級別的程序來說,有兩點非常重要。
首先第一點,定義機器級別程序的格式和行為被稱為 指令集體系結構或指令集架構(instruction set architecture)
, ISA。ISA 定義了進程狀態、指令的格式和每一個指令對狀態的影響。大部分的指令集架構包括 ISA 用來描述進程的行為就好像是順序執行的,一條指令執行結束后,另外一條指令再開始。處理器硬件的描述要更復雜,它可以同時並行執行許多指令,但是它采用了安全措施
來確保整體行為與 ISA 規定的順序一致。
第二點,機器級別對內存地址的描述就是 虛擬地址(virtual address)
,它提供了一個內存模型來表示一個巨大的字節數組。
編譯器在整個編譯的過程中起到了至關重要的作用,把 C 語言轉換為處理器執行的基本指令。匯編代碼非常接近於機器代碼,只不過與二進制機器代碼相比,匯編代碼的可讀性更強,所以理解匯編是理解機器工作的第一步。
一些進程狀態對機器可見,但是 C 語言程序員卻看不到這些,包括
程序計數器(Program counter)
,它存儲下一條指令的地址,在 x86-64 架構中用%rip
來表示。
程序執行時,PC 的初始值為程序第一條指令的地址,在順序執行程序時, CPU 首先按程序計數器所指出的指令地址從內存中取出一條指令,然后分析和執行該指令,同時將 PC 的值加 1 並指向下一條要執行的指令。
比如下面一個例子。

這是一段數值進行相加的操作,程序啟動,在經過編譯解析后會由操作系統把硬盤中的程序復制到內存中,示例中的程序是將 123 和 456 執行相加操作,並將結果輸出到顯示器上。由於使用機器語言難以描述,所以這是經過翻譯后的結果,實際上每個指令和數據都可能分布在不同的地址上,但為了方便說明,把組成一條指令的內存和數據放在了一個內存地址上。
- 整數
寄存器文件(register file)
包含 16 個命名的位置,用來存儲 64 位的值。這些寄存器可以存儲地址和整型數據。有些寄存器用於跟蹤程序狀態,而另一些寄存器用於保存臨時數據,例如過程的參數和局部變量,以及函數要返回的值。這個文件
是和磁盤文件無關的,它只是 CPU 內部的一塊高速存儲單元。有專用的寄存器,也有通用的寄存器用來存儲操作數。 條件碼寄存器
用來保存有關最近執行的算術或邏輯指令的狀態信息。這些用於實現控件或數據流中的條件更改,例如實現 if 和 while 語句所需的條件更改。我們都學過高級語言,高級語言中的條件控制流程主要分為三種:順序執行、條件分支、循環判斷
三種,順序執行是按照地址的內容順序的執行指令。條件分支是根據條件執行任意地址的指令。循環是重復執行同一地址的指令。- 順序執行的情況比較簡單,每執行一條指令程序計數器的值就是 + 1。
- 條件和循環分支會使程序計數器的值指向任意的地址,這樣一來,程序便可以返回到上一個地址來重復執行同一個指令,或者跳轉到任意指令。
下面以條件分支為例來說明程序的執行過程(循環也很相似)

程序的開始過程和順序流程是一樣的,CPU 從 0100 處開始執行命令,在 0100 和 0101 都是順序執行,PC 的值順序+1,執行到 0102 地址的指令時,判斷 0106 寄存器的數值大於 0,跳轉(jump)到 0104 地址的指令,將數值輸出到顯示器中,然后結束程序,0103 的指令被跳過了,這就和我們程序中的 if()
判斷是一樣的,在不滿足條件的情況下,指令會直接跳過。所以 PC 的執行過程也就沒有直接+1,而是下一條指令的地址。
- 一組
向量寄存器
用來存儲一個或者多個整數或者浮點數值,向量寄存器是對一維數據上進行操作。
機器指令只會執行非常簡單的操作,例如將存放在寄存器的兩個數進行相加,把數據從內存轉移到寄存器中或者是條件分支轉移到新的指令地址。編譯器必須生成此類指令的序列,以實現程序構造,例如算術表達式求值,循環或過程調用和返回
認識匯編
我相信各位應該都知道匯編語言的出現背景吧,那就是二進制表示數據,太復雜太龐大了,為了解決這個問題,出現了匯編語言,匯編語言和機器指令的區別就在於表示方法上,匯編使用操作數
來表示,機器指令使用二進制來表示,我之前多次提到機器碼就是匯編,你也不能說我錯,但是不准確。
但是匯編適合二進制代碼存在轉換關系的。
匯編代碼需要經過 匯編器
編譯后才產生二進制代碼,這個二進制代碼就是目標代碼,然后由鏈接器將其連接起來運行。

匯編語言主要分為以下三類
- 匯編指令:它是一種機器碼的
助記符
,它有對應的機器碼 - 偽指令:沒有對應的機器碼,由編譯器執行,計算機並不執行
- 其他符號,比如 +、-、*、/ 等,由編譯器識別,沒有對應的機器碼
匯編語言的核心是匯編指令,而我們對匯編的探討也是基於匯編指令展開的。
與匯編有關的硬件和概念
CPU
CPU 是計算機的大腦,它也是整個計算機的核心,它也是執行匯編語言的硬件,CPU 的內部包含有寄存器,而寄存器是用於存儲指令和數據的,匯編語言的本質也就是 CPU 內部操作數所執行的一系列計算。
內存
沒有內存,計算機就像是一個沒有記憶的人類,只會永無休止的重復性勞動。CPU 所需的指令和數據都由內存來提供,CPU 指令經由內存提供,經過一系列計算后再輸出到內存。
磁盤
磁盤也是一種存儲設備,它和內存的最大區別在於永久存儲,程序需要在內存裝載后才能運行,而提供給內存的程序都是由磁盤存儲的。
總線
一般來說,內存內部會划分多個存儲單元,存儲單元用來存儲指令和數據,就像是房子一樣,存儲單元就是房子的門牌號。而 CPU 與內存之間的交互是通過地址總線
來進行的,總線從邏輯上分為三種
- 地址線
- 數據線
- 控制線

CPU 與存儲器之間的讀寫主要經過以下幾步
讀操作步驟
- CPU 通過地址線發出需要讀取指令的位置
- CPU 通過控制線發出讀指令
- 內存把數據放在數據線上返回給 CPU
寫操作步驟
- CPU 通過地址線發出需要寫出指令的位置
- CPU 通過控制線發出寫指令
- CPU 把數據通過數據線寫入內存
下面我們就來具體了解一下這三類總線
地址總線
通過我們上面的探討,我們知道 CPU 通過地址總線
來指定存儲位置的,地址總線上能傳送多少不同的信息,CPU 就可以對多少個存儲單元進行尋址。

上圖中 CPU 和內存中間信息交換通過了 10 條地址總線,每一條線能夠傳遞的數據都是 0 或 1 ,所以上圖一次 CPU 和內存傳遞的數據是 2 的十次方。
所以,如果 CPU 有 N 條地址總線,那么可以說這個地址總線的寬度是 N 。這樣 CPU 可以尋找 2 的 N 次方個內存單元。
數據總線
CPU 與內存或其他部件之間的數據傳送是由數據總線
來完成的。數據總線的寬度決定了 CPU 和外界的數據傳輸速度。8 根數據總線可以一次傳送一個 8 位二進制數據(即一個字節)。16 根數據總線一次可以傳輸兩個字節,32 根數據總線可以一次傳輸四個字節。。。。。。
控制總線
CPU 與其他部件之間的控制是通過 控制總線
來完成的。有多少根控制總線,就意味着 CPU 提供了對外部器件的多少種控制。所以,控制總線的寬度決定了 CPU 對外部部件的控制能力。
一次內存的讀取過程
內存結構
內存 IC 是一個完整的結構,它內部也有電源、地址信號、數據信號、控制信號和用於尋址的 IC 引腳來進行數據的讀寫。下面是一個虛擬的 IC 引腳示意圖

圖中 VCC 和 GND 表示電源,A0 - A9 是地址信號的引腳,D0 - D7 表示的是控制信號、RD 和 WR 都是好控制信號,我用不同的顏色進行了區分,將電源連接到 VCC 和 GND 后,就可以對其他引腳傳遞 0 和 1 的信號,大多數情況下,+5V 表示1,0V 表示 0。
我們都知道內存是用來存儲數據,那么這個內存 IC 中能存儲多少數據呢?D0 - D7 表示的是數據信號,也就是說,一次可以輸入輸出 8 bit = 1 byte 的數據。A0 - A9 是地址信號共十個,表示可以指定 00000 00000 - 11111 11111 共 2 的 10次方 = 1024個地址
。每個地址都會存放 1 byte 的數據,因此我們可以得出內存 IC 的容量就是 1 KB。
如果我們使用的是 512 MB 的內存,這就相當於是 512000(512 * 1000) 個內存 IC。當然,一台計算機不太可能有這么多個內存 IC ,然而,通常情況下,一個內存 IC 會有更多的引腳,也就能存儲更多數據。
內存讀取過程
下面是一次內存的讀取過程。

來詳細描述一下這個過程,假設我們要向內存 IC 中寫入 1byte 的數據的話,它的過程是這樣的:
- 首先給 VCC 接通 +5V 的電源,給 GND 接通 0V 的電源,使用
A0 - A9
來指定數據的存儲場所,然后再把數據的值輸入給D0 - D7
的數據信號,並把WR(write)
的值置為 1,執行完這些操作后,即可以向內存 IC 寫入數據 - 讀出數據時,只需要通過 A0 - A9 的地址信號指定數據的存儲場所,然后再將 RD 的值置為 1 即可。
- 圖中的 RD 和 WR 又被稱為控制信號。其中當WR 和 RD 都為 0 時,無法進行寫入和讀取操作。
總結
此篇文章我們主要探討了指令集、指令集的分類,與匯編有關的硬件,總線都有哪些,分別的作用都是什么,然后我們以一次內存讀取過程來連接一下 CPU 和內存的交互過程。
原創不易,如有幫助還請各位讀者四連(點在、在看、分享、留言),感謝各位大佬
關注公眾號 程序員cxuan 回復 cxuan 領取優質資料。
我自己寫了六本 PDF ,非常硬核,鏈接如下
我自己寫了六本 PDF ,非常硬核,鏈接如下
我自己寫了六本 PDF ,非常硬核,鏈接如下