l 32位64位操做系統是由CPU寄存器的位數決定,即虛擬尋址的範圍爲2^32、2^64。html
l 字節的大端小端法是以字節爲基本單位的:好比十進制的7在十六位機器上表示node
· 數組
地址緩存 |
100less |
101函數 |
大端法性能 |
00000000測試 |
00000111優化 |
小端法spa |
00000111 |
00000000 |
l 大部分編譯器默認進行算數右移和用補碼錶示覆數
l 對於移位k來講,實際移位量爲k mod 2^w(w爲所移動數據類型的位數)
l 對於整型常量來講c編譯器都是先將其當作正數,若是前面有 - 運算符則對其去負即轉換成補碼錶示。
l //這形成了C中及其隱晦的存在
l //limits.h
l #define INT_MAX 2147483647 // 二進制表示方法爲0111 1111 1111 1111,爲C最大表示的整型數2^31 - 1
l #define INT_MIN (-INT_MAX - 1)
l /*
l 爲何不直接寫成-2147483648呢? 這是由於編譯器在看到這個常量時,不會先看取負元算符,而是看到了2147483648即2^31,編譯器會感受到沒法用int型來表示,便會將其用長整形來表示爲unsigned int 1000 0000 0000 0000,這樣它的類型就爲unsigned,在你進行使用的時候便會發生錯誤,考慮以下代碼
l */
l #include <limits.h>
l #include <stdio.h>
l #include <stdlib.h>
l
l int main()
l {
l int a = 5;
l
l printf("%d", a > -2147483648); // result is 0, 這是由於比較過程當中發生了隱式轉換,轉換成了unsigned int 之間的比較。
l printf("%d", a > INT_MIN); // result is 1,二者都是int類型,因此比較成功
l return 0;
l }
這就是C語言中,負數最小值爲-2147483648,卻不能直接寫出的緣由。具體請參考http://www.cnblogs.com/Jack47/archive/2013/01/06/TMin32-in-c.html
l 浮點數:
n 32位浮點數由一位符號位s,8位階碼exp即權值,23位尾數
數值類型 |
s |
exp |
尾數f |
規格化 |
s |
!=0 && != 255 |
f(尾數定義爲1+f), 階碼爲exp - 01111111 |
非規格化 |
s |
0000 0000 |
尾數爲f(尾數定義爲f, exp爲1 - 01111111)緣由見下 |
無窮大 |
s(1,0分別表示正負無窮大) |
1111 1111 |
000000000000000....... |
NaN(NOT a NUMBER) |
s |
1111 1111 |
!=0 |
n 緣由保證最小規格化數到最大規格化數的平穩過渡,這樣最小規格化數的exp爲0000 0001 - 0111 1111 = -126;最大非規格化數的exp爲1 - 0111 1111 = -126. 這樣就保證了二進制小數數值均勻的接近於零
n 64位浮點數一位符號位s, 11位階碼, 52位尾數, 原理相同.
n 浮點數的舍入(多采用向偶數舍入, 又稱其爲向最接近的值舍入): 當值不位於要舍入的中間值時, 向最近的整數舍入; 當值爲中間值時, 向最接近的偶數舍入. 好比1.5舍入爲整數, 由於其位於一和二中間, 向偶數舍入, 爲2.
l 知識準備:32位系統寄存器
名稱 |
特殊做用 |
|
%eax |
一般做爲函數返回值, |
低十六位組成%ax, %ax中高八位爲%ah,低八位爲%al |
%ecx |
|
同上 |
%edx |
|
同上 |
%ebx |
|
同上 |
%esi |
|
只有低十六位的 |
%edi |
|
同上 |
%esp |
棧指針 |
同上 |
%ebp |
幀指針 |
同上 |
l 操做數(R表示取寄存器的值, M(addr)表示對地址addr的引用
類型 |
格式 |
操做數值 |
例子 |
當即數 |
$imm |
imm |
push $imm: 入棧imm |
寄存器 |
%e |
R[e] |
push %eax: 寄存器%eax的值入棧 |
存儲器 |
(imm) |
M[imm] |
push (4): 地址4處的值入棧 |
存儲器 |
(%e) |
M[R[e]] |
push (%eax): 以%eax的值爲地址的值入棧 |
存儲器 |
imm(%e) |
M[R[e] + imm] |
... |
存儲器 |
(Ea, Eb) |
M[R[Ea]+ R[Eb]] |
... |
存儲器 |
imm(Ea, Eb) |
M[R[Ea]+ R[Eb] + imm] |
... |
存儲器 |
(, Ei, s) |
M[R[Ei] * s] |
... |
存儲器 |
(Eb, Ei, s) |
M[R[Eb]+ R[Ei] * s] |
... |
存儲器 |
imm(Eb, Ei, s) |
M[R[Eb]+ R[Ei] * s + imm] |
最經常使用 |
l 數據傳送指令:MOV(S, Z), S和D不能同時爲存儲器目標
n Mov[b, w, l]:分別傳送[字節, 字, 雙字],
n Movb S, D: 將S傳送到D
n Movs[bw, bl, wl]: 符號擴展
n Movz[bw, bl, wl]: 零擴展
n Pushl: 雙字入棧, eg, push %eax
n Popl: 雙字出棧, eg, pop %eax
l 算術和邏輯操做
n Leal S, D: &SàD
n INC D: D++
n DEC D: D--
n DEG D: -D
n NOT D: ~D (取補)
n ADD S, D: D = D + S
n SUB S, D: D = D - S
n IMUL S, D: D = S * D
n XOR S, D: D = D ^ S (異或)
n OR S, D: D = D | S
n AND S, D: D = D & S
n SAL(SHL) k, D: D = D << k
n SAR k, D: D = D >> k (算數右移)
n SHR k, D: D = D >> k (邏輯右移)
l 特殊的算數操做
n imull S: 某個寄存器 = S * R[%eax] (有符號)
n mull S: (無符號)
n cltd S: R[%eax] 進行符號擴展到某個寄存器
n idivl S: 有符號除法, R[%eax] / S; 商放到%eax中, 餘數放到%edx中
n divl S: 無符號除法
l 控制:條件, 循環
n 條件(根據條件碼來設置)
u cmp[b, w, l]: 分別比較字節, 字, 雙字. cmp S1 S2(基於 S2 - S1), 會設置條件碼
u set[e(equal), n(not), s(負數), g(great), l(less), a(above), b(below)] D: 根據條件碼集合將D設置爲1或0.
u test S1, S2(基於S1 & S2, 只改變條件碼, 一般用來測試S1 ?= S2)
u j[…與set相同, 額外有mp]: jmp LABLE(直接跳轉到LABLE), jmp *op(間接跳轉到對op解引用後, 以解引用後的值進行跳轉)
u 條件,循環控制多借助於上述組合來使用:eg
u test %edx, %eax
u je .L3 若是相等則跳轉到.L3
u .L3
u 僞代碼
u cmp %edx, %eax
u jne .L3 若是不相等則跳轉到.L3
u .L3
u 僞代碼
n 條件傳送指令: 有利於現代處理器更好的執行, 避免分支懲罰
u 相似於 x < y ? y - x : x - y, 語句能夠產生條件傳送指令
u 配合上文的條件碼, cmov[與set相同], cmovl S, R 若小於則傳送
n switch語句配合跳轉表來使用, 只進行一次條件判斷
n call Label(* Operand): 過程調用, 將返回地址入棧, 並跳轉到被調用過程的起始地址
n ret: 與call指令做用相反
l 消除循環的低效率
n 對於循環中的過程調用盡可能移出循環外, 例如:
n for (i = 0; i < strlen(s); i++) //strlen()函數爲線性增加,在字符串長度很大時,很消耗系統資源
n 減小沒必要要的存儲器引用, 將存儲器引用儲存在臨時變量中.
l 處理器優化: 即充分利用存儲器流水線操做的吞吐量
n 循環展開, 減小讀寫相關, 即所使用的數據必須等待上一次操做完成.
n 從新結合變換, 減小讀寫相關, eg
n for ()
acc = (acc OP data[i]) OP data[i + 1]
// acc = acc OP (data[i] OP data[ i + 1] 相比上一行, 處理器能夠更新acc的同時進行(data[i] OP data[i + 1]), 而上一行就必須等待acc更新完以後才能進行(acc OP data[i])操做.
l 利用局部性原理: 高速緩存
n 時間局部性: 被引用過一次的存儲器位置可能在不遠的時間內繼續被引用
n 空間局部性: 若是一個存儲器位置被引用了, 極可能在不遠的未來被引用其附近的存儲器位置
n 每一級緩存只是關心其上下級緩存的分組狀況, 緩存管理多是硬件自己, 或者軟件, 亦或是兩者的結合
l 內核爲每一個進程維護一個描述符表, 描述符表(每一個進程獨有)是一個結構指針數組, 每一個指針指向打開文件表, 打開文件表(包含當前文件位置, 引用計數等, 全部進程共享)包含一個指向v_node表的指針, v_node表也是包含一些文件基本信息(全部進程共享). 值得注意的是, 內核默認爲每一個進程打開標準輸出, 標準輸入, 標準錯誤輸出三個文件, 若是任何一個打開文件表的引用計數變爲零, 內核則會收回打開文件表中此文件的內存, 在對其引用可能會發生意想不到的錯誤. 子進程與父進程不一樣且繼承的是描述符表, 同時系統藉助描述符表和虛擬存儲器來實現文件共享.