man -k:
經常使用來搜索,結合管道使用。例句以下:linux
man -k k1 | grep k2 | grep 2
搜索同時含有k1和k2,且屬於系統調用。編程
最後的數字意味着幫助手冊中的區段,man手冊共有8個區段,最經常使用的是123,含義以下:vim
1.Linux 2.系統調用 3.c語言
可是當單獨用man語句的時候,想查看其中的單獨某個區段內的解釋時,用法是這樣的:windows
man 3 printf
即查找c語言中printf的用法。數組
grep -nr
這條語句能夠用來查找關鍵字,全文搜索,而且能夠直接查找文件內的內容。其中:緩存
n:爲顯示行號 r:爲遞歸查找
例如,若是想查找某個宏,咱們已知宏保存在include文件夾中,因此可使用下列語句:安全
grep -nr XXX /usr/include(XXX爲所要找的宏)
cheat
cheat是很是好用的「打小抄」搜索工具,可以方便的告訴你你想要的內容。編程語言
vim
vim是一種很是好用的編輯器,總共有六種基本模式,最經常使用的是普通模式、插入模式和命令行模式。須要熟悉這三種模式之間的切換方式:編輯器
普通→插入: i 或 a 插入→普通: Esc 或 Ctrl + [ 普通→命令行: : 命令行→普通:Esc 或 Ctrl + [
經常使用的進入、保存和退出指令:函數
進入:vim 文件名 保存:命令行模式 :w 退出:命令行模式 :q
經常使用動做:
刪除:dd刪除整行 複製:yy複製整行 粘貼:p
※實用功能:交換上下行——ddp,快速交換光標所在行與它下面的行。
vim的功能很強大,而且能夠移植到不少種不一樣的程序中,可是咱們如今使用的過程當中真正用到的命令不多也很簡單.
gcc
經常使用選項
-c 只編譯不連接,生成目標文件.o -S 只編譯不彙編,生成彙編代碼 -E 只進行預編譯,不作其餘處理 -g 在可執行程序中包含標準調試信息 -o file 將file文件指定爲輸出文件 -v 打印出編譯器內部編譯各過程的命令行信息和編譯器的版本 -I dir 在頭文件的搜索路徑列表中添加dir目錄
編譯過程
預處理:gcc –E hello.c –o hello.i; gcc –E調用cpp 生成中間文件 編 譯:gcc –S hello.i –o hello.s; gcc –S調用ccl 翻譯成彙編文件 匯 編:gcc –c hello.s –o hello.o; gcc -c 調用as 翻譯成可重定位目標文件 鏈 接:gcc hello.o –o hello ; gcc -o 調用ld** 建立可執行目標文件 -o後面是接的你給生成的文件指定的名字,若是不指定,則默認爲a.out
在命令行上運行這個可執行目標文件須要輸入它的名字:
./a.out
其中./表明當前目錄。
gdb
注意:使用GCC編譯時要加「-g」參數,而後纔可以用gdb調試
GDB最基本的命令有:
gdb programm(啓動GDB) l 查看所載入的文件 b 設斷點 info b 查看斷點狀況 run 開始運行程序 bt 打印函數調用堆棧 p 查看變量值 c 從當前斷點繼續運行到下一個斷點 n 單步運行(不進入) s 單步運行(進入) quit 退出GDB
四種斷點:
1.行斷點 b [行數或函數名] <條件表達式> 2.函數斷點 b [函數名] <條件表達式> 3.條件斷點 b [行數或函數名] <if表達式> 4.臨時斷點 tbreak [行數或函數名] <條件表達式>
另外的調試工具:
cgdb,有單獨的debug窗口,更方便查看 ddd,圖形頁面
Make和Makefile
這是實現自動化編譯的好方法。
Makefile的通常寫法:
一個Makefile文件主要含有一系列的規則,每條規則包含如下內容:
須要由make工具建立的目標體,一般是可執行文件和目標文件,也能夠是要執行的動做,如‘clean’;
要建立的目標體所依賴的文件,一般是編譯目標文件所須要的其餘文件。
建立每一個目標體時須要運行的命令,這一行必須以製表符TAB開頭
格式爲:
test(目標文件): prog.o code.o(依賴文件列表) tab(至少一個tab的位置) gcc prog.o code.o -o test(命令) ....... 即: target: dependency_files command
定義變量的兩種方式:
(1)遞歸展開方式 VAR=var (2)簡單方式 VAR:=var
使用變量的格式爲:
$(VAR)
靜態庫:.a(linux)、.lib(windows) 動態庫:.so(linux)、.dll(windows) 靜態庫(以linux爲例)
建立該庫:
gcc -c addvec.c multvec.c ar rcs libvector.a addvec.o multvec.o
涉及到的參數所作動做:
gcc -c只編譯,不鏈接成爲可執行文件。 即:把.c文件編譯成.o文件 ar -r:在庫中插入模塊(替換) -c:建立一個庫 -s:寫入一個目錄文件索引到庫中 即:把兩個.o文件歸檔成靜態庫存檔文件.a而且寫入目錄文件索引到庫中
建立它的可執行文件
gcc -02 -c main2.c gcc -static -o p2 main2.o ./libvector.a
相關參數含義:
gcc -02 和-0都是代碼優化指令,能夠減小編譯時間 -c 只編譯,不鏈接成爲可執行文件 -static 告訴編譯器驅動程序,連接器應該構建一個徹底連接的可執行目標文件 -o 命名生成文件
動態庫(linux)
構造建立共享庫:
gcc -shared -fPIC -o libvector.so addvec.c multvec.c
參數解析:
-fPIC 指示編譯器生成與位置無關的代碼 -shared 指示連接器建立一個共享的目標文件 -o 命名生成文件 把.c文件編譯成爲.o文件,放入新建的共享庫中,而且命名。
連接程序
gcc -o p2 main2.c ./libvector.so
建立一個可執行目標文件p2,在運行時能夠和動態庫libverctor.so連接。
這一章介紹了一些基本的概念,是之後各章的總括,提到的內容都在以後的各章中拆開細講,我認爲這章最重要的就是一句話:
信息=位+上下文
計算機中的信息都是有二進制數字表達的,而由於這些二進制位所處的位置不一樣,是有符號數仍是無符號數,是大端法仍是小端法,因爲具體的解釋不一樣,形成的結果也不一樣。
以後的學習就是如何讀寫位,和上下文如何對應。
這章裏我以爲最容易混淆的是小端法和大端法。經常使用小端法,巧記方式是「高對高,低對低」,可是同時要注意字節在存放的時候的高低與咱們慣常認知中的高低位的關係,以及一串數據中幾位表明一個字節:
一個字節是8位,也就是兩位十六進制數。
而後是整數中,有符號數和無符號數的表示,補碼錶示,位運算和邏輯運算,溢出、截斷和擴展;
浮點數中,二進制小數,最重要的是IEEE小數:
IEEE浮點標準:
用V=(-1)^s X 2^E X M 來表示一個數: 符號:s決定這個數是正仍是負。0的符號位特殊狀況處理。 階碼:E對浮點數加權,權重是2的E次冪(可能爲負數) 尾數:M是一個二進制小數,範圍爲1~2-ε或者0~1-ε(ε=1/2的n次冪)
編碼規則:
單獨符號位s編碼符號s,佔1位 k位的階碼字段exp編碼階碼E n位小數字段frac編碼尾數M(同時須要依賴階碼字段的值是否爲0)
兩種精度
單精度(float),k=8位,n=23位,一共32位; 雙精度(double),k=11位,n=52位,一共64位。
三種被編碼狀況
規格化的 非規格化的 特殊值
還有舍入方式:
1.向偶舍入(默認方法) 即:將數字向上或向下舍入,是的結果的最低有效數字爲偶數。 能用於二進制小數。 2.向零舍入 即:把整數向下舍入,負數向上舍入。 3.向下舍入 正數和負數都向下舍入。 4.向上舍入 正數和負數都向上舍入。
默認的(即向偶舍入)方法能夠獲得最接近的匹配,其他三種可用於計算上界和下界。
這一章和以前學習過的彙編很像,須要注意的就是尋址方式和幾個操做,mov,push,pop,leal,還有跳轉指令、控制轉移指令等等。
幾個比較新的容易遺忘的知識點是:
翻譯循環
彙編中能夠用條件測試和跳轉組合起來實現循環的效果,可是大多數彙編器中都要先將其餘形式的循環轉換成do-while格式。
1.do-while循環 通用形式: do body-statement while(test-expr); 循環體body-statement至少執行一次。 能夠翻譯成: loop: body-statement t = test-expr; if(t) goto loop; 即先執行循環體語句,再執行判斷。 2.while循環 通用形式: while (test-expr) body-statement GCC的方法是,使用條件分支,表示省略循環體的第一次執行: if(!test-expr) goto done; do body-statement while(test-expr); done: 接下來: t = test-expr; if(!t) goto done: loop: body-statement t = test-expr; if(t) goto loop; done: 歸根究底,仍是要把循環改爲do-while的樣子,而後用goto翻譯。
3.for循環
for循環能夠輕易的改爲while循環,因此再依照上面的方法改爲do-while再翻譯便可。
寄存器使用慣例
程序寄存器組是惟一能被全部過程共享的資源。
這個慣例是爲了防止一個過程P調用另外一個過程Q時寄存器中的值被覆蓋。慣例以下:
%eax,%edx,%ecx 調用者保存寄存器(Q可覆蓋,P的數據不會被破壞) %ebx,%esi,%edi 被調用者保存寄存器(Q在覆蓋這些值前必須壓入棧並在返回前回復他們) %ebp,%esp 慣例保持 %eax用來保存返回值
幀棧結構
這裏最基本的是要知道,針對每一句彙編語句,它都幹了些什麼,寄存器裏的值如何變化,棧幀如何變化,內存中的值如何變化,更要明白的是每一行的變化都表明着什麼。
我如今欠缺的就是這一點,解讀每一句是對存儲器或者寄存器作了什麼動做不難,可是要明白爲何這麼作,這麼作對應着高級編程語言中的什麼,就顯得生疏,須要多加練習。
這章裏學習的是一個相對簡單的處理器Y86,指令集比起IA32省略了不少。學習中我以爲一個比較不容易掌握,作題時須要反覆查閱的是:
指令的字節級編碼
每條指令須要1-6個字節不等,每條指令的第一個字節代表指令的類型。
1.第一個字節
這個字節分爲兩個部分,每一個部分4位:
高四位:代碼部分,值域爲0~0xB
第四位:功能部分,功能值只有在一組相關指令共用一個代碼時纔有用。
好比:課本第233頁,Y86指令集的功能碼:
整數操做裏代碼部分均爲6,功能部分區分addl,subl,andl,xorl
分支指令裏代碼部分均爲7
傳送指令裏代碼部分均爲2
這裏要注意rrmovl歸到了傳送指令裏,前面說過它們有相同的指令代碼
8個程序寄存器中每一個都有相應的0~7的寄存器標識符,程序寄存器存在CPU中的一個寄存器文件中,這個寄存器文件就是一個小的、以寄存器id做爲地址的隨機訪問存儲器。
當須要指明不該訪問任何寄存器時,用ID值0xF表示
2.有的須要額外的字節
(1)附加的寄存器指示符字節
指定一個或兩個寄存器,例如rA或者rB。
沒有寄存器操做數的指令,例如分支指令和call指令,就沒有寄存器指示符字節。
只須要一個寄存器操做數的指令(irmovl,pushl,popl)將另外一個寄存器指示符設爲0xF
(2)附加的4字節常數字
這個字的用處:
1.irmovl的當即數數據 2.rmmol和mrmovl的地址指示符的偏移量 3.分支指令和調用指令的目的地址
注意事項
1.分支指令和調用指令的目的地址是一個絕對地址 2.全部整數採用小端法編碼
指令編碼這一部分能夠幫助咱們理解機器碼與彙編代碼的對應關係,而且涉及到下一個我認爲的難點:
Y86的順序實現
這裏的六個階段和每一個階段進行的動做,得出的數據和對應的結果看起來彷佛眼花繚亂,可是課本上已經給出了部分經常使用命令的具體過程,課後的做業題中也有過相似的做業,這要求咱們的觸類旁通的能力。
至於HDL語言,在EDA課程中已經有過基礎,看起來卻是比較輕鬆。
涉及到ROM,RAM,磁盤的幾個計算,總線等等。概念理解起來比較簡單,計算只須要套公式,注意不要漏算盤面或者柱面就好。
這一章我以爲最重要的是局部性。
局部性原理:
一個編寫良好的計算機程序,經常傾向於引用臨近於其餘最近引用過的數據項的數據項,或者最近引用過的數據項自己。
分類:
時間局部性 空間局部性
量化評價一個程序中局部性的簡單原則:
重複引用同一個變量的程序有良好的時間局部性 對於具備步長爲k的引用模式的程序,步長越小,空間局部性越好 對於取指令來講,循環有好的時間和空間局部性。循環體越小,循環迭代次數越多,局部性越好。 還有存儲器層次結構,這裏最重要的思想是這句話:每層存儲設備都是下一層的「緩存」。
這樣就涉及到了緩存命中和不命中的狀況,還有替換策略:
1.緩存命中 當程序須要第k+1層的某個數據對象d時,首先在當前存儲在第k層的一個塊中查找d,若是d恰好緩存在第k層中,就稱爲緩存命中。 該程序直接從第k層讀取d,比從第k+1層中讀取d更快。 2.緩存不命中 即第k層中沒有緩存數據對象d。 這時第k層緩存會從第k+1層緩存中取出包含d的那個塊。若是第k層緩存已滿,就可能會覆蓋現存的一個塊 覆蓋——替換/驅逐
替換策略:
隨機替換策略-隨機犧牲一個塊 最近最少被使用替換策略LRU-犧牲最後被訪問的時間距離如今最遠的塊。
3.緩存不命中的種類
(1)強制性不命中/冷不命中
即第k層的緩存是空的(稱爲冷緩存),對任何數據對象的訪問都不會命中。
(2)衝突不命中
因爲一個放置策略:將第k+1層的某個塊限制放置在第k層塊的一個小的子集中,這就會致使緩存沒有滿,可是那個對應的塊滿了,就會不命中。
(3)容量不命中
當工做集的大小超過緩存的大小時,緩存會經歷容量不命中,就是說緩存過小了,不能處理這個工做集。
而後是高速緩存存儲器。
高速緩存是一個高速緩存組的數組,它的結構能夠用元組(S,E,B,m)來描述: S:這個數組中有S=2^s個高速緩存組 E:每一個組包含E個高速緩存行 B:每一個行是由一個B=2^b字節的數據塊組成的 m:每一個存儲器地址有m位,造成M=2^m個不一樣的地址
除此以外還有標記位和有效位:
有效位:每一個行有一個有效位,指明這個行是否包含有意義的信息 標記位:t=m-(b+s)個,惟一的標識存儲在這個高速緩存行中的塊 組索引位:s 塊偏移位:b 高速緩存的結構將m個地址劃分紅了t個標記位,s個組索引位和b個塊偏移位。
轉眼間本學期已通過去了一半,以爲本身最大的收穫就在於獨立學習能力的提升。雖然從上學期的Java就開始進行每週自學寫博客這種學習方式,可是明顯感受到這學期適應了不少,開學初看到這本比上學期還厚的《深刻理解計算機系統》確實以爲難度很大,翻看了裏面的內容,彙編、EDA、C語言......多種學科的混合,但我沒有被這些困難嚇住而止步不前,而是每週看老師給出的教學進程進行每週的學習,這樣一週周下來,我驚喜地發現其實個人自學能力有了很大的提升,每週老師給出的重點題目都會認真去作,不太懂的題目會和周圍的小夥伴討論,經過課後習題的完成來熟悉本章節的內容。
經過這門課的學習,以爲本身抓重點的能力也有所提高,一般一週學習一章,而一週的內容一般都有幾十頁有時甚至上百頁,這樣多的學習任務乍一看以爲很難完成,可是若是經過課後題來看這章的重點的話就會輕鬆很多,也許好幾頁的課本只是圍繞着一個公式進行講解,經過課後題會用並理解就能夠了。
這半學期的學習帶給個人不只僅是知識,而是更爲重要的學習能力,但願在以後的半學期裏我能夠收穫到更多~
以爲最大的不足就在於不夠深刻地理解每個知識點,每週的學習基本都是根據老師畫的重點題目來的,有些題目裏沒有提到的知識點我也許會看也不看,重點題目也會看答案,瞭解一下是怎樣的計算過程,也許有時都不會去追究爲何要這樣作,更多的是去想老師會怎麼考,這樣是不利於知識點的掌握的,之後會多加註意,深刻理解。
由於以爲自身動手實踐能力比較薄弱,因此想說,老師可不能夠在課堂上多增長一些實際操做演練環節,讓咱們看一看操做起來一步步要怎樣去作,效果如何,這樣可能會更直觀一些,印象也會更深入。而理論知識的講解,就我我的而言,多是學的不夠透徹,有些並不能徹底理解,因此我以爲轉換成實際操做可能會更容易理解一些~