沒(méi)錯(cuò)!cxuan 對(duì)匯編下手了
匯編代碼是計(jì)算機(jī)的一種低級(jí)表示,它是一種低級(jí)語(yǔ)言,可以從字面角度去理解它,包括處理數(shù)據(jù)、管理內(nèi)存、讀寫存儲(chǔ)設(shè)備上的數(shù)據(jù),以及利用網(wǎng)絡(luò)通信等。編譯器生成機(jī)器碼經(jīng)過(guò)了一系列的轉(zhuǎn)換,這些轉(zhuǎn)換遵循編程語(yǔ)言、目標(biāo)機(jī)器的指令集 和操作系統(tǒng)。
指令集
指令集就是指揮計(jì)算機(jī)工作的指令,因?yàn)槌绦蚓褪前凑找欢▓?zhí)行順序排列的指令。因?yàn)橛?jì)算機(jī)的執(zhí)行控制權(quán)由 CPU 操作,所以指令集就是 CPU 中用來(lái)計(jì)算和控制計(jì)算機(jī)的一系列指令的集合。每個(gè) CPU 在產(chǎn)出時(shí)都規(guī)定了與硬件電路相互配合工作的指令集。
指令集有不少分類,但是一般分為兩種,一種是精簡(jiǎn)指令集,一種是復(fù)雜指令集。具體描述如下
精簡(jiǎn)指令集
精簡(jiǎn)指令的英文是 reduced instruction set computer, RISC,原意是精簡(jiǎn)指令集計(jì)算,簡(jiǎn)稱為精簡(jiǎn)指令集,是 CPU 的一種 設(shè)計(jì)模式,可以把 CPU 想象成一家流水線工廠,對(duì)指令數(shù)目和尋址方式都做了精簡(jiǎn),使其實(shí)現(xiàn)更容易,指令并行執(zhí)行程度更好,編譯器的效率更高。
常見的精簡(jiǎn)指令集處理器包括 ARM、AVR、MIPS、PARISC、RISC-V 和 SPARC。
所以你就能理解

這本書是講啥的了。
它主要是基于 MIPS 體系結(jié)構(gòu)把馮諾依曼體系的五大組件進(jìn)行了逐一的硬件實(shí)現(xiàn) + 軟件設(shè)計(jì)介紹,更為重要的是引入了諸多并行計(jì)算的內(nèi)容,這是大部分教材中忽略或者內(nèi)容較少的,會(huì)根據(jù)這個(gè)思路把并行相關(guān)的內(nèi)容,結(jié)合 OpenMP, CUDA 和 Hadoop/Spark 整體融入到新書中,畢竟這是未來(lái)發(fā)展的趨勢(shì)
還有這本書

這本書又是講啥的。
這本書是講 RISC-V 指令集的,因?yàn)橹噶罴牟煌矃^(qū)分了三個(gè)版本,三個(gè)版本???嗯,還有下面這個(gè)

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

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

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

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

匯編語(yǔ)言主要分為以下三類
匯編指令:它是一種機(jī)器碼的 助記符,它有對(duì)應(yīng)的機(jī)器碼偽指令:沒(méi)有對(duì)應(yīng)的機(jī)器碼,由編譯器執(zhí)行,計(jì)算機(jī)并不執(zhí)行 其他符號(hào),比如 +、-、*、/ 等,由編譯器識(shí)別,沒(méi)有對(duì)應(yīng)的機(jī)器碼
匯編語(yǔ)言的核心是匯編指令,而我們對(duì)匯編的探討也是基于匯編指令展開的。
與匯編有關(guān)的硬件和概念
CPU
CPU 是計(jì)算機(jī)的大腦,它也是整個(gè)計(jì)算機(jī)的核心,它也是執(zhí)行匯編語(yǔ)言的硬件,CPU 的內(nèi)部包含有寄存器,而寄存器是用于存儲(chǔ)指令和數(shù)據(jù)的,匯編語(yǔ)言的本質(zhì)也就是 CPU 內(nèi)部操作數(shù)所執(zhí)行的一系列計(jì)算。
內(nèi)存
沒(méi)有內(nèi)存,計(jì)算機(jī)就像是一個(gè)沒(méi)有記憶的人類,只會(huì)永無(wú)休止的重復(fù)性勞動(dòng)。CPU 所需的指令和數(shù)據(jù)都由內(nèi)存來(lái)提供,CPU 指令經(jīng)由內(nèi)存提供,經(jīng)過(guò)一系列計(jì)算后再輸出到內(nèi)存。
磁盤
磁盤也是一種存儲(chǔ)設(shè)備,它和內(nèi)存的最大區(qū)別在于永久存儲(chǔ),程序需要在內(nèi)存裝載后才能運(yùn)行,而提供給內(nèi)存的程序都是由磁盤存儲(chǔ)的。
總線
一般來(lái)說(shuō),內(nèi)存內(nèi)部會(huì)劃分多個(gè)存儲(chǔ)單元,存儲(chǔ)單元用來(lái)存儲(chǔ)指令和數(shù)據(jù),就像是房子一樣,存儲(chǔ)單元就是房子的門牌號(hào)。而 CPU 與內(nèi)存之間的交互是通過(guò)地址總線來(lái)進(jìn)行的,總線從邏輯上分為三種
地址線 數(shù)據(jù)線 控制線

CPU 與存儲(chǔ)器之間的讀寫主要經(jīng)過(guò)以下幾步
讀操作步驟
CPU 通過(guò)地址線發(fā)出需要讀取指令的位置 CPU 通過(guò)控制線發(fā)出讀指令 內(nèi)存把數(shù)據(jù)放在數(shù)據(jù)線上返回給 CPU
寫操作步驟
CPU 通過(guò)地址線發(fā)出需要寫出指令的位置 CPU 通過(guò)控制線發(fā)出寫指令 CPU 把數(shù)據(jù)通過(guò)數(shù)據(jù)線寫入內(nèi)存
下面我們就來(lái)具體了解一下這三類總線
地址總線
通過(guò)我們上面的探討,我們知道 CPU 通過(guò)地址總線來(lái)指定存儲(chǔ)位置的,地址總線上能傳送多少不同的信息,CPU 就可以對(duì)多少個(gè)存儲(chǔ)單元進(jìn)行尋址。

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

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

來(lái)詳細(xì)描述一下這個(gè)過(guò)程,假設(shè)我們要向內(nèi)存 IC 中寫入 1byte 的數(shù)據(jù)的話,它的過(guò)程是這樣的:
首先給 VCC 接通 +5V 的電源,給 GND 接通 0V 的電源,使用 A0 - A9來(lái)指定數(shù)據(jù)的存儲(chǔ)場(chǎng)所,然后再把數(shù)據(jù)的值輸入給D0 - D7的數(shù)據(jù)信號(hào),并把WR(write)的值置為 1,執(zhí)行完這些操作后,即可以向內(nèi)存 IC 寫入數(shù)據(jù)讀出數(shù)據(jù)時(shí),只需要通過(guò) A0 - A9 的地址信號(hào)指定數(shù)據(jù)的存儲(chǔ)場(chǎng)所,然后再將 RD 的值置為 1 即可。 圖中的 RD 和 WR 又被稱為控制信號(hào)。其中當(dāng)WR 和 RD 都為 0 時(shí),無(wú)法進(jìn)行寫入和讀取操作。
總結(jié)
此篇文章我們主要探討了指令集、指令集的分類,與匯編有關(guān)的硬件,總線都有哪些,分別的作用都是什么,然后我們以一次內(nèi)存讀取過(guò)程來(lái)連接一下 CPU 和內(nèi)存的交互過(guò)程。
原創(chuàng)不易,如有幫助還請(qǐng)各位讀者四連(點(diǎn)在、在看、分享、留言),感謝各位大佬
完
往期推薦
??

