閱碼場Ftrace公開課火熱報(bào)名中:Ftrace公開課:學(xué)優(yōu)化,學(xué)內(nèi)核(限50人)。課程首發(fā)2日報(bào)名累計(jì)18個(gè),剩余32個(gè)名額,先到先得,報(bào)名咨詢客服(小月微信:linuxer2016)。
這是我3月2日視頻分享的文字稿,從匯編語言的本質(zhì),學(xué)習(xí)匯編語言的意義講到學(xué)習(xí)方法。
關(guān)鍵詞
分享 知識(shí)系統(tǒng) 匯編語言 軟件 系統(tǒng)軟件 芯片 中間件 內(nèi)核 系統(tǒng)調(diào)用 編程模型 匯編 應(yīng)用層 反匯編 編譯器 邏輯運(yùn)算 操作系統(tǒng) 軟硬件接口 體系結(jié)構(gòu)
以下是文字記錄
〇、作者簡介
在正文之前給先大家做一個(gè)簡單的自我介紹,自我介紹和后面的內(nèi)容也是有一定聯(lián)系的。我大學(xué)學(xué)的是微電子專業(yè),也就是芯片專業(yè)。從2005年讀研開始做單片機(jī)的開發(fā),后來又去做Linux的系統(tǒng)移植。從那之后基本上一直都是做軟件,芯片的東西也有關(guān)注,但是沒有具體做過,最后一次具體做項(xiàng)目,應(yīng)該是讀研的時(shí)候?qū)嶒?yàn)室做過一些芯片相關(guān)的東西。所以我其實(shí)是沒有接受過一個(gè)特別完整的計(jì)算機(jī)專業(yè)的受訓(xùn)經(jīng)歷的,很多東西都是用到了或者遇到問題的時(shí)候再去學(xué)習(xí)。我在學(xué)習(xí)的時(shí)候特別注重兩點(diǎn),一點(diǎn)是注重知識(shí)的系統(tǒng),另一點(diǎn)是重視回到原始的文檔。一、匯編語言的本質(zhì)
今天的分享里面也會(huì)涉及到知識(shí)的系統(tǒng)和原始文檔這兩點(diǎn)。首先我們?yōu)槭裁匆囊灰獙W(xué)習(xí)匯編語言,因?yàn)槲覀儾还茉趯W(xué)習(xí)任何知識(shí)的時(shí)候,一定都要有兩個(gè)視角。一個(gè)是在這個(gè)知識(shí)里面,搞清楚這個(gè)知識(shí)本身是怎么回事。另外一個(gè)就是跳出這個(gè)知識(shí)或者這個(gè)技能來看看,從外面往里面看,看看這是什么東西。所以今天的分享,我們先從外往里面先看一下。首先匯編語言是個(gè)什么東西,按照我的理解,匯編語言其實(shí)就是計(jì)算機(jī)軟件和硬件的接口。所以這個(gè)接口意味著什么呢,意味著我們往下走,可以去了解計(jì)算機(jī)體系結(jié)構(gòu),我們往上走,可以去了解操作系統(tǒng)軟件。二、學(xué)習(xí)匯編語言的意義
我自己一直是在做系統(tǒng)軟件的開發(fā),我的工作經(jīng)驗(yàn)中其實(shí)有很多都是操作系統(tǒng)相關(guān)的軟件。這里面有幾個(gè)項(xiàng)目都是直接或者間接的用到了匯編語言的技能,給大家先舉幾個(gè)例子。第一個(gè)例子是,大概十年前我在中星微工作的時(shí)候,當(dāng)時(shí)負(fù)責(zé)芯片平臺(tái)的CPU和環(huán)境軟件的支持。其中有一項(xiàng)任務(wù)是機(jī)臺(tái)測試,機(jī)臺(tái)測試是什么呢,就是我們要去支持在芯片的生產(chǎn)線上去篩選合格和不合格的芯片,這里面需要提供測試用例,有一部分代碼必須要使用匯編去寫,這個(gè)是我第一次手寫匯編語言。不僅僅是我在這個(gè)經(jīng)歷里面手寫了匯編語言,同時(shí)為了手寫匯編語言還要去了解函數(shù)之間是怎么調(diào)用的。之前我也知道 ABI 這個(gè)概念,ABI 就是 Application Binary lnterface 應(yīng)用程序的二進(jìn)制接口。我知道這個(gè)概念,但是并不確切知道函數(shù)調(diào)用之間要符合什么樣的規(guī)范。我也是在那次的機(jī)臺(tái)測試的時(shí)候具體的看了ARM的AAPCS手冊,也就是ARM Architecture Procedure Call Standard 的ABI手冊。我了解到這個(gè)手冊之后,我才對(duì)這個(gè)函數(shù)調(diào)用之間關(guān)系有一個(gè)系統(tǒng)的了解。這個(gè)也對(duì)應(yīng)到前面我們說的點(diǎn),就是要注重知識(shí)的系統(tǒng)和原始的文檔。第二個(gè)例子是,我在SUSE工作的時(shí)候遇到的,大家都知道SUSE是第二大的Linux發(fā)行版公司,第一大的是RedHat。我當(dāng)時(shí)是做虛擬化,主要負(fù)責(zé)的是中間件的東西,主要是libvirt和QEMU,也有一點(diǎn)點(diǎn)Xen的工作。這個(gè)工作本身它是比較靠上層的,那里面主要的libvirt和QEMU都是用C語言寫的,這可以看成是一個(gè)C語言的應(yīng)用開發(fā),這個(gè)工作本身幾乎是不涉及到匯編語言的。但是那時(shí)候發(fā)生個(gè)事情,就是我們在某一個(gè)場景下面,發(fā)現(xiàn)那個(gè)系統(tǒng)啟動(dòng)有問題。熟悉虛擬化的同學(xué)都知道,QEMU里面的系統(tǒng)啟動(dòng)方式跟我們物理機(jī)可以一樣,也可以不一樣。我當(dāng)時(shí)就發(fā)現(xiàn)這個(gè)問題很奇怪,因?yàn)槲抑耙恢弊鯝RM的東西,對(duì)x86平臺(tái)其實(shí)是不了解的。此時(shí)我有兩個(gè)選擇一個(gè)選擇,一個(gè)是去看x86相關(guān)的資料,看一看BIOS這個(gè)流程怎么樣,啟動(dòng)流程怎么樣。但那時(shí)候我看了之后發(fā)現(xiàn)Google直接得到資料一般都不太具體,它比較抽象,講的都是大概的流程,沒有具體的東西。這個(gè)沒法支持到我怎么去解決問題,于是那個(gè)時(shí)候我就嘗試著通過匯編語言去看一下BIOS那個(gè)代碼。我發(fā)現(xiàn),它原來是在某個(gè)地方蹦到某個(gè)地址,再做跳轉(zhuǎn),從而完成了從BIOS到下一級(jí)BootLoader的啟動(dòng)。第三個(gè)例子就是,我在華為工作的時(shí)候做一個(gè)跟 ABI相關(guān)的事情,我之前也提到過, 就是AARCH64的ILP32的工作,相當(dāng)于是我在64位的系統(tǒng)里面去用匯編,但是我要支持32位的ELF。這個(gè)里面基本上來說因?yàn)榫幾g器當(dāng)時(shí)已經(jīng)差不多做好了,雖然有些bug,但是基本上是沒問題的。因?yàn)槲耶?dāng)時(shí)解決的主要是要完善glibc跟內(nèi)核的這個(gè)工作本身幾乎不涉及到匯編語言,除了 ld.so和系統(tǒng)調(diào)用,例如系統(tǒng)調(diào)用中我要考慮怎么去給應(yīng)用程序給系統(tǒng)將傳參。但是在這個(gè)項(xiàng)目里面,我發(fā)現(xiàn)了解匯編語言還是很有幫助的,為什么呢,因?yàn)槟闳ソ庖恍゜ug的時(shí)候,有些地方如果對(duì)匯編語言不了解,會(huì)很不方便。比如說那時(shí)候我們是大端系統(tǒng),我們首先就要考慮兩個(gè)問題,就是大小端的轉(zhuǎn)換。我們既要保證這個(gè)代碼大端是正確的,要保證小端是正確的。第二個(gè)事我們要考慮32位和64位的轉(zhuǎn)換,因?yàn)槲矣脩舳耸?2位的ELF,進(jìn)到內(nèi)核里面是64位的,這兩個(gè)地方它經(jīng)常會(huì)出一些問題。這個(gè)時(shí)候除了做代碼級(jí)的分析之外,另外一個(gè)方式就是看反匯編,看看它這個(gè)地方32位和64位的關(guān)系到底是怎么處理的。從這三個(gè)例子大家可以看到匯編語言其實(shí)不僅僅是我們做底層開發(fā),比如開發(fā)操作系統(tǒng),或者說調(diào)內(nèi)核調(diào)驅(qū)動(dòng)的時(shí)候需要,它其實(shí)也是我們?nèi)ダ斫釩PU的編程模型,去理解系統(tǒng)的一個(gè)很好的切入點(diǎn)。所以回到我們最原始的問題,要不要學(xué)匯編語言。我自己的體會(huì)是,如果工作是涉及到系統(tǒng)軟件和中間件,這個(gè)是CPU相關(guān)的,這個(gè)匯編語言是要學(xué)習(xí)的。如果不是這樣,這種工作可能比如說是JAVA 或者別的語言,我覺得相關(guān)的像ByteCode的這種相應(yīng)字節(jié)碼的一些相關(guān)的知識(shí)可能也是需要的。三、如何學(xué)習(xí)匯編語言
我們前面主要聊了一下要不要學(xué)匯編語言,我們下一段主要是分享如何學(xué)習(xí)匯編語言。那聯(lián)想一下,前面咱們做內(nèi)核知識(shí)的分享,當(dāng)時(shí)我們分享的時(shí)候提到一個(gè)點(diǎn)就是軟件的分層。大家應(yīng)該對(duì)分層這點(diǎn)都比較熟悉了,我們在學(xué)習(xí)匯編語言的時(shí)候,也可以借助這樣一個(gè)思想,我先學(xué)習(xí)什么樣的匯編語言呢,先學(xué)習(xí)哪個(gè)層次跟相關(guān)的匯編語言呢,顯然應(yīng)用層可能會(huì)比較容易一點(diǎn)。剛開始去看匯編的時(shí)候,可以先看一看這個(gè)應(yīng)用層匯編是怎么回事,應(yīng)用層匯編包括什么內(nèi)容,我們就可以回到基本的程序設(shè)計(jì)語言,它基本結(jié)構(gòu)是怎么樣的。就是這個(gè)順序判斷循環(huán)對(duì)吧,它這里面就包括了算術(shù)運(yùn)算和邏輯運(yùn)算,還包括判斷和跳轉(zhuǎn)這些基本的指令,有了這些指令之后,我們可以在比如說我們涉及匯編語言和C語言的調(diào)用,匯編語言和C++的調(diào)用,比如說涉及到的一些 ABI 相關(guān)的事情,我們可以有一個(gè)匯編的切入點(diǎn)可以去進(jìn)行查看。同時(shí),如果說crash了或者coredump了或者內(nèi)核 crash 了,我們借助匯編語言,可以去看一下具體出錯(cuò)那個(gè)地方,因?yàn)榉磪R編得到的C語言的行號(hào)可能沒有那么準(zhǔn)確。那除此之外,我們在了解了算數(shù)運(yùn)算邏輯運(yùn)算這些基本的匯編之后,還可以再去深入的學(xué)習(xí)。在這個(gè)地方我們先停留一下,我們怎么去學(xué)呢,比較直接的辦法當(dāng)然是我們找一些資料去學(xué)習(xí),這肯定是沒問題的。還有什么辦法呢,比如我去看體系架構(gòu)的手冊,比如看x86、ARM、RISC-V的架構(gòu)手冊,我們?nèi)タ催@些手冊里面具體是怎么寫的。我覺得這是一個(gè)最終的目標(biāo),如果最終我們能夠基于這些手冊去解決我們匯編語言相關(guān)的問題,那么這個(gè)匯編語言的學(xué)習(xí)就比較到位了,可以說是已經(jīng)到了一個(gè)可以自己進(jìn)行自我迭代的一個(gè)程度了。問題在于我們怎么能夠進(jìn)入到這樣一個(gè)狀態(tài),直接去看這些架構(gòu)手冊的話還是非常有挑戰(zhàn)性的。那么我們學(xué)習(xí)匯編語言的方式應(yīng)該是什么呢,這個(gè)方式就可以是,我們自己寫段代碼,不要開啟編譯器的-O3等各種優(yōu)化選項(xiàng),然后看看反匯編是什么樣子的。這樣我們就能對(duì)基本的匯編語言有一個(gè)基本的了解。當(dāng)然在實(shí)際工作中,我們可能會(huì)開啟 -O3 -Os這些編譯選項(xiàng),這些都沒關(guān)系的。我們再去對(duì)比它開與不開有什么區(qū)別,匯編指令有哪些不同,這樣我們就有了第一手的素材,對(duì)匯編語言就有了一個(gè)基本的了解。同時(shí)在這個(gè)層次上可能還會(huì)出現(xiàn)一個(gè)什么情況,我們可能有時(shí)候會(huì)看到一些內(nèi)嵌匯編,那看到內(nèi)嵌匯編,比如說是GCC的內(nèi)嵌匯編,根據(jù)我們前面說的我的學(xué)習(xí)方法,我們還要回到原始的文檔。那么GCC的匯編語言,就要去看GCC的手冊,GCC關(guān)于內(nèi)嵌匯編有很詳細(xì)的說明,詳細(xì)的說明里面我們能不能根據(jù)詳細(xì)的說明去理解,我該怎么去寫這些GCC的內(nèi)嵌匯編的。同時(shí)這個(gè)地方涉及到一些ABI的知識(shí),就是什么情況下我需要用那些extra的一些flag(參見參考資料1)。那么這個(gè)地方你可以看到它其實(shí)又是一個(gè)軟硬件的一個(gè)接口,我們其實(shí)是在學(xué)習(xí)匯編語言,其實(shí)看的是我們對(duì)于系統(tǒng)軟件的了解,對(duì)于 CPU 架構(gòu)的了解。那么我們對(duì)于簡單的匯編如算術(shù)邏輯運(yùn)算判斷跳轉(zhuǎn)有了一個(gè)了解之后,我們再往下走是什么呢。我們會(huì)看到一些比如說操作內(nèi)核,它用的匯編跟應(yīng)用的匯編有什么區(qū)別。區(qū)別就是操作系統(tǒng)內(nèi)核是一個(gè)privilege,特權(quán)級(jí)別,這個(gè)特權(quán)級(jí)別它就會(huì)對(duì)應(yīng)到一些特權(quán)相關(guān)的指令。比如說如何進(jìn)入和退出一個(gè)特權(quán)級(jí)別,比如說如何去操作一些 CPU 的控制寄存器。這些控制寄存器可能涉及到操作系統(tǒng)的一些能力,比如說中斷使能、系統(tǒng)調(diào)用的路由、中斷路由這些事情。在這個(gè)地方其實(shí)我們有可以看到,匯編語言和CPU架構(gòu)其實(shí)是高度相關(guān)的。也就是說我們可以在學(xué)習(xí)某個(gè)CPU架構(gòu)的時(shí)候,我們?nèi)?duì)照匯編去看一些操作系統(tǒng)比如Linux、RTT,它是怎么去寫這個(gè)匯編語言的。同時(shí)我們也可以基于某一次對(duì)操作系統(tǒng)比如Linux的一個(gè)調(diào)試,我們基于調(diào)試中我們看到一些我們之前沒有見過的匯編的一些語言,再切入回來,再去看一看它跟CPU架構(gòu)有時(shí)么關(guān)系,哪些是跟CPU架構(gòu)相關(guān)的。除了我們剛剛說的系統(tǒng)分層這塊之外,系統(tǒng)分層里面還有什么內(nèi)容呢。比如說Memory模型,像ARM和RISC-V,它都是統(tǒng)一尋址的,如果是x86,它其實(shí)還有傳統(tǒng)的方式,還包括一個(gè)IO尋址的方式。這種東西它在匯編語言里面也有會(huì)有所區(qū)別,同時(shí)在Memory模型里面還有一塊頁表處理。頁表處理里面頁表基址在什么地方,有幾個(gè)頁表基址,這些又都涉及到匯編語言去訪問特殊功能的寄存器。那通過這樣一個(gè)學(xué)習(xí)呢,我們基本來說對(duì)于匯編語言就有了一個(gè)整體的了解了,不管是應(yīng)用掛了還是操作系統(tǒng)內(nèi)核掛了我們都可以去調(diào)試。那這個(gè)是不是就學(xué)完了呢,還沒有,為什么呢,因?yàn)檫€有一個(gè)很重要的概念就是pseudo assembly,就是偽匯編。偽匯編這個(gè)東西,我們平時(shí)用得非常廣泛。大家平時(shí)不知道有沒有遇到過這樣的問題,我看到一段代碼,但是我去搜手冊就是沒搜到這個(gè)匯編在哪,但是它明明是匯編器能編譯過的,還是能用的一個(gè)商業(yè)/開源軟件。它為什么就不知道在哪了,那這個(gè)就是偽匯編,對(duì)吧。其實(shí)它并不單獨(dú)對(duì)應(yīng)某一條機(jī)器指令,它其實(shí)是某個(gè)匯編指令的一個(gè)變化,或者是多條指令的集合。那偽匯編其實(shí)是跟架構(gòu)相關(guān)的,不同的CPU架構(gòu)它會(huì)不一樣。比如說RISC-V,它其實(shí)在RISC-V spec里面是有寫到的。它也有專門的ASM手冊,里面也會(huì)單獨(dú)寫都有哪些偽匯編指令。了解偽匯編指令,其實(shí)是很重要的,就是看我們的匯編語言學(xué)習(xí)到底是不是實(shí)操,到底我只是知道紙面上知道這些匯編語言,還是說我真正在工程里面調(diào)過匯編語言。因?yàn)楣こ汤锩嫠泻芏喽际莻螀R編了,如果我沒有看位匯編的話,那么可能在實(shí)際工程中看別人寫的代碼就會(huì)比較費(fèi)勁。OK, 剛剛說到基本的算術(shù)邏輯運(yùn)算判斷跳轉(zhuǎn),還有特權(quán)指令,Memory模型、偽匯編,除此之外匯編語言里面還有什么內(nèi)容呢?其實(shí)還有一些內(nèi)容,比如說像x86的3DNow ?MMX這種多媒體指令。還有ARM的NEON指令,VFP的這種浮點(diǎn)指令。這些指令它也會(huì)對(duì)應(yīng)的匯編。這些匯編和前面的邏輯差不多,我們也是要看基本的應(yīng)用是怎么用的,然后再看它還要考慮一點(diǎn),整數(shù)跟這種浮點(diǎn)之間它相互調(diào)用是怎么操作的,這個(gè)地方我就不展開講了。四、推薦的書籍與文檔
所以可以看到,其實(shí)我們在整個(gè)學(xué)習(xí)過程中,我覺得比較重要的一點(diǎn)就是注重回到原始文檔。原始文檔有什么,比如說 CPU的架構(gòu)手冊,比如操作系統(tǒng)的代碼,比如操作系統(tǒng)的一些重要的書籍。我是建議大家日常至少應(yīng)該有兩本,一本是講 CPU講計(jì)算機(jī)組成原理的書,比如說《計(jì)算機(jī)組成原理與設(shè)計(jì)軟硬件接口》那本書,然后還有一塊應(yīng)該就是操作系統(tǒng)的書了,比如說我最近正在看陳海波老師寫的《現(xiàn)代操作系統(tǒng)》。我覺得經(jīng)典的書都可以,也不用說去糾結(jié)到底看哪一本。我的建議是我們對(duì)照CPU和操作系統(tǒng)的兩本書去看,我們會(huì)發(fā)現(xiàn)有些內(nèi)容CPU的書里面講的比較多,有些內(nèi)容操作系統(tǒng)的書里面講得比較多一點(diǎn)。我們要不斷的注重回到原始的文檔,除了計(jì)算機(jī)體系結(jié)構(gòu)和操作系統(tǒng)的書之外呢,平時(shí)可能還需要看的就是這個(gè)ABI相關(guān)的這些手冊。大家可以趁這個(gè)機(jī)會(huì)去看一看,大家自己平時(shí)用的常用的CPU就是 x86、ARM、RISC-V,那這里面哪些資料你可以再去看一看。大家可以先把自己手頭的資料給它補(bǔ)齊,補(bǔ)齊之后呢我們基于問題再去對(duì)應(yīng)相應(yīng)的資料,最終目標(biāo)應(yīng)該是能夠看到原始的文檔去解決問題。五、總結(jié)
這個(gè)就是我今天的分享。總結(jié)一下,今天我是從自我介紹開始的。因?yàn)槲沂寝D(zhuǎn)專業(yè)的,所以我會(huì)比較重視知識(shí)的系統(tǒng)和原始的文檔。今天我們是從匯編語言是軟硬件接口開始分享。我首先舉了三個(gè)我自己的項(xiàng)目經(jīng)歷,有些是明顯要寫匯編的,有些是看起來跟匯編語言無關(guān),但是由于為了解決問題去看了匯編語言。還有一些,是跟體系結(jié)構(gòu)操作系統(tǒng)有點(diǎn)關(guān)系,會(huì)比較多地涉及到通過匯編語言做調(diào)試。這也就是說,系統(tǒng)軟件包括中間件這些軟件可能我們在調(diào)試的時(shí)候了解匯編語言,對(duì)我們的工作可能會(huì)或多或少的有一些幫助。第二部分我分享的是如何學(xué)習(xí)匯編語言。我們站在分層的角度先去學(xué)習(xí)應(yīng)用層次的匯編語言。然后再往深了一層去看特權(quán)級(jí)別的匯編語言有什么不一樣,這涉及到一些特權(quán)指令,涉及到Memory模型的一些內(nèi)容,最后我們說偽匯編和浮點(diǎn)指令這些內(nèi)容。我是張健,前華為架構(gòu)師,前創(chuàng)業(yè)公司技術(shù)合伙人,目前自由職業(yè)。同時(shí)我是兩個(gè)孩子的爸爸。在職業(yè)發(fā)展,工作和生活如何平衡方面有很多第一手經(jīng)驗(yàn)。歡迎大家與我探討。- https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
往期精華文章:【精華】Linux閱碼場原創(chuàng)精華文章匯總
閱碼場付費(fèi)會(huì)員專業(yè)交流群
會(huì)員招募:各專業(yè)群會(huì)員費(fèi)為88元/季度,權(quán)益包含群內(nèi)提問,線下活動(dòng)8折,全年不定期群技術(shù)分享(普通用戶直播免費(fèi),分享后每次點(diǎn)播價(jià)為19元/次),有意加入請私信客服小月(小月微信號(hào):linuxer2016)
專業(yè)群介紹:
彭偉林-閱碼場內(nèi)核性能與穩(wěn)定性本群定位內(nèi)核性能與穩(wěn)定性技術(shù)交流,覆蓋云/網(wǎng)/車/機(jī)/芯領(lǐng)域資深內(nèi)核專家,由閱碼場資深講師彭偉林主持。
甄建勇-Perf?Monitor&Perf?Counter
本群定位Perf、cache和CPU架構(gòu)技術(shù)交流,覆蓋云/網(wǎng)/車/機(jī)/芯領(lǐng)域資深用戶,由閱碼場資深講師甄建勇主持。
鄧世強(qiáng)-Xenomai與實(shí)時(shí)優(yōu)化
本群定位Xenomai與實(shí)時(shí)優(yōu)化技術(shù)交流,覆蓋云/網(wǎng)/車/機(jī)/芯領(lǐng)域資深用戶,由閱碼場資深講師鄧世強(qiáng)和彭偉林共同主持。
周賀賀-Tee和ARM架構(gòu)
本群定位Tee和ARM架構(gòu)技術(shù)交流,覆蓋云/網(wǎng)/車/機(jī)/芯領(lǐng)域資深用戶,由閱碼場資深講師周賀賀主持。