《深入理解電腦系統》筆記(一)【插圖】

來源:互聯網
上載者:User
讀後感

        這本書是美國“卡內基-梅隆大學(CMU)”的教科書,邏輯嚴謹。雖然是教科書,還是有些晦澀難懂啊,不太形象。第二章主要講整數,浮點數,很是晦澀,全是數學公式。作者的思維數學的思維,動不動就是n、m、k、∑等等,讓我們數學很爛的同學如何是好。如果能以普通人的思維把數學知識加進去就好了。

        該書確實系統的介紹了電腦,很完善。它能給你以下幾個重要層級的模型和過程:

    1.函數的調用棧模型——第三章(函數不一定都會建立棧幀)

    2.a.out或者exe可執行檔的結構——第七章點擊開啟連結

    3.程式載入器和連結——第八章 點擊開啟連結

    4.malloc和虛擬儲存空間原理——第九章點擊開啟連結

    5.線程,在儲存空間中模型——第12章

    對於處於成長期的程式員來說,真是欣喜若狂!

    有了這些還需要《C專家編程》嗎?哈哈!

    翻譯者很是用心,但是讀者不一定領情。比如:可以直接翻譯流行的記憶體、硬碟和固態硬碟,完全沒有必要用主存、磁碟和固態儲存磁碟。還比如:沒有必要把shell翻譯成“外殼”多彆扭啊。這些翻譯者應該像“侯捷”學習。

    這本說內容大而散,感覺沒有盡頭一樣。老外怎麼學這種課程,費腦子啊。從電腦結構、二進位表示、到組合語言函數的調用、然後cpu的結構、再有連接器儲存空間、還有進程,並發、更有網路編程,基本大學四年也就學了這麼多東西。

    這本書中有句話很有意思:儲存空間的一個有趣的屬性是不論系統中有多大的儲存空間,他總是一種稀缺資源。磁碟空間和垃圾桶同樣有這個屬性。

    工作2年多的時間裡,每每都是在網上搜系統方面的知識、編譯、連結和虛擬儲存空間malloc等等。只有讀了這本書才能系統得學到電腦知識。

一、電腦漫遊

---》利用直接儲存空間(DMA)的技術,資料可以不通過cpu而直接從磁碟到達記憶體。

---》根據機械原理,較大的儲存空間比較小的儲存空間運行慢,一個寄存器只能儲存幾百個Byte,而且記憶體可以存放GB以上。加快處理器的運行速度比加快記憶體運行速度更容易。

---》快取至關重要,一個簡單的helloworld揭示了一個重要的問題。系統花費大量時間把資訊從一個地方挪到另一個地方。helloworld最初放在硬碟上,然後載入到記憶體,進而進入cpu中。說明了一個儲存空間階層:

二、資訊的表示和處理

講的是電腦原理,二進位,補碼和浮點數等。因為大學課程已經學習過了,沒有細讀。

---》浮點數,規格化、非規格化和無窮大。

    一般來說我們沒把發用小數表示1/3、7/10等這些不能整出的數字,那麼如果用二進位表示十進位的小數,更多的表示不出來。二進位甚至不能表示十進位的0.1和0.2

三、程式的機器級表示(其實就是組合語言)

---》講的是《組合語言》,頭都大了!個人覺得組合語言不用花時間瞭解,即使是本書中的組合語言也有文字解析。IA32X86-64兩種組合語言。

---》彙編代碼不區分有符號和無符號甚至指標類型。

---》展示了,彙編代碼尾碼的含義:

    大多數GCC產生的彙編指令都有一個字元尾碼,表示運算元的大小。例如資料傳送指令有三個變種:movb(傳送位元組)、movw(傳送字)和movl(傳送雙字)。注意,彙編代碼使用尾碼'l'來表示4個位元組整數和8個位元組雙精確度浮點數,這不會產生歧義,因為浮點數使用的是一組完全不同的指令和寄存器。

    運算元指示符,運算元一共有三類:1)立即數(immediate)也就是常數值,立即數的書寫方式是$。例如:$0x1F。2)寄存器,3)儲存空間(memory).由於三種運算元的存在所以定址方式就有很多種。

---》一個32位cpu中寄存器的結構如下:

    是IA32的整數寄存器。所有8個寄存器都可以作為32位和16位使用,例如%eax和%ax。並且前四個寄存器可以訪問其兩個低位元組。如:%ah和%al。

是64位cpu的寄存器結構圖:

紅色框內,是相容32為cpu的結果。

---》寄存器使用慣例:%eax、%edx和%ecx是調用者儲存寄存器,%ebx、%esi和%edi是被調用者儲存寄存器。那麼,一個函數f()可能被別人調用,也可以調用其他函數,所以當f()運行時需要將%ebx、%esi和%edi儲存到棧中,並在返回前再恢複它們。p(151)---》64位%rax寄存器用來儲存函數的傳回值,p(198)

    在x86-64組合語言,中%rax用來儲存函數的傳回值,而在結果返回之前,%rax可以重複利用。

---》棧在處理函數調用中起到至關重要的作用。棧的,棧頂朝下,由於IA32 的棧竟然是往低地址延伸生長,直讓我崩潰。(p115)

圖片的上半部分,說明了實際效果,即將%eax的值移動到%edx中,圖片的下半部分是棧移動步驟。棧頂的變化最後關鍵。從0x108 -> 0x104 -> 0x108

---》棧幀結構,IA32程式用程式棧來支援函數調用。機器用棧來傳遞函數參數、傳回值、儲存寄存器用於以後恢複和本機存放區。為單個過程分配的那部分棧成為棧幀(stack frame)。說了棧幀的結構。

---》call指令。call指令的效果是將傳回值地址入棧,並跳轉到被調用過程的起始處。返回地址是在程式中緊跟在call後面的那條指令的地址。這樣當被調用函數返回時,執行會從此處繼續。ret指令從棧中彈出地址,並跳轉到這個位置。例如下面的代碼:

int accum = 0;int sum(int x,int y);int main(){    return sum(1.3);}int sum(int x,int y){    int t = x + y;    accum += t;    return t;}

經過反組譯碼後,節選處call部分的代碼如所示:

第一行call指令的效果就是將0x80483e1壓入棧中,同時將%eip(程式計數器)的值設定為sum的第一條指令0x8048394.最後一行的ret指令彈出0x80483e1給%eip,並跳轉到這個地址。:

ret指令的效果就是讓0x080483e1彈出,調整棧指標,並且0x080483e1賦給%eip,程式繼續執行。

---》函數調用執行個體

int swap_add(int* xp,int* yp);int caller(){    int arg1 = 534;    int arg2 = 1057;    int sum = swap_add(&arg1 , &arg2);    int diff = arg1 - arg2;    retur sum * diff;}int swap_add(int* xp,int* yp){    int x = * xp;    int y = * yp;    *xp = y;    *yp = x;    return x + y;}


    (藍色箭頭是“指向”,紅色箭頭是“位移量”,綠色箭頭是解釋說明)

    arg1和arg2必須存放在棧中,因為我們必須為它們產生地址。swap_add中的變數int x和int y可以存放在寄存器中。

    分配在棧上的24個位元組,8個用於局部變數,8個用於參數,8個未使用,這是因為GCC認識所有的棧空間都應該是16的整數倍。這樣保證資料放的嚴格對齊。

    經過調用swap_add之後棧的資訊又恢複到最初的狀態。

---》許多函數編譯後不需要棧幀。如果所有的局部變數都能儲存在寄存器中,而且這個函數又不會調用其他函數(葉子過程),那麼需要棧的唯一原因就是用來儲存傳回值。特別是dui'yu所以,雖然C語言中有寄存器變數,但是如果這個函數的變數很少的話,及時不標明這個變數是寄存器,它也會被載入到寄存器中去。(p196)

---》函數需要棧幀的原因有如下幾個:

    ●局部變數太多,不能都放在寄存器中。

    ●有些局部變數是數組或者結構。

    ●函數用&來計算一個局部變數的地址。

    ●函數必須將棧上的某些參數傳遞給另外一個函數

    ●在修改一個被調用著儲存寄存器之前,函數需要儲存其他狀態。

---》棧破壞檢測和棧保護p(181)

    在C語言中,沒有可靠的方法來防止對數組的越界寫操作。數組越界,是棧溢出後發現這個錯誤然後拋出。

    

echo是一個函數,存放了char buf[8]的一個局部變數。

思想:在棧幀中任何局部緩衝區與棧狀態之間儲存一個特殊的金絲雀(canary)值,也成為哨兵值(guard value)是在程式每次運行時隨機產生的。因此如果這個哨兵值改變了說明棧溢出了。

---》棧隨機化p(180)

    電腦

    比如,多次運行下面的代碼,本地變數的地址是不變的。

    int main()

{

    int local;

    printf("local at %p\n",&local);

    return 0;

    一個現實生活中的例子,但是這個例子說的是每次堆上開闢空間可能是一致的。

    曾經在做Symbian項目的時候,發現一個不是必現的bug,後來發現是野指標。但是問題是為什麼不是必現呢?是因為Symbian作業系統每次在上開闢的空間,在短時間內是一個地址。舉例:假如,ptr這個指標,現在成為野指標了。但是,之後它指向的記憶體又被重新malloc了,等同於ptr指向了新的對象。但是,這個巧合并不是每次複現。

---》將IA32擴充到64位。p(183)

    X86-64是AMD提出來,並命名的。現在一般簡寫X64

    ●通用目的寄存器組從8個擴充到16個。而且名字也變成了%rax,%rbx。其中%rax用來存放傳回值。

    ●許多程式狀態都儲存在寄存器中,而不是棧上。整形和指標類型的參數通過寄存器傳遞。所以,有些過程根本不需要建立棧。

    ●如果可能,條件操作作用條件傳送指令實現,會得到比傳統分支代碼更好的效能。

    ●浮點操作用面向寄存器的指令集來實現,而不是IA32支援的基於棧的方法來實現。

    ●X86-64沒有幀寄存器。

---》函數指標的值是該函數機器代碼錶示中的第一條指令的地址。(p173)

剩餘的章節繼續新的blog

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.