深入理解電腦系統(第二版)讀書筆記

來源:互聯網
上載者:User

 

 

八月初開始接觸《深入理解電腦系統》這本書,當時看的是英文版,花了一個月的時間大體上過了一遍,但我知道其實還是有很多沒看懂的地方,於是借了一本中文版,如今總算可以說我真正看過了這本書,可以寫一寫讀書筆記了。

深入理解電腦系統讀書筆記

p39-40
把位元轉為無符號十進位數 B2U(w)=∑xi*2^i (0<=i<=w-1)
B2U(4)([1011])=1*2^3+0*2^2+1*2^1+1*2^0=11

轉換為補碼形式 B2T(w)=-x(w-1)*2^(w-1)+∑xi*2^i (0<=i<=w-2)
B2T(4)([1011])=-1*2^3+0*2^2+1*2^1+1*2^0=-5

p45-46
無符號與補碼之間轉換
T2U(w)=x+2^w (x<0) 
=x (x>=0)

U2T(w)=u (u<2^(w-1))
=u-2^w (u>=2^(w-1))

p52
無符號數的截斷結果 B2U([x(k-1),x(k-2)...x0])=B2U([x(w-1)...x0])mod 2^k
補碼數字截斷結果 B2T([x(k-1),x(k-2)...x0])=U2T(B2U([x(w-1)...x0])mod 2^k)

p55
無符號加法 x+(u,w)y= x+y (x+y<2^w)
= x+y-2^w (2^w<=x+y<=2^(w+1))

p58
補碼加法  x+(t,w)y= x+y-2^w (2^(w-1)<=x+y) 正溢出
=x+y (-2^(w-1)<=x+y<2^(w-1)) 正常
=x+y+2^w (x+y<-2^(w-1)) 負溢出

p64
乘積移位操作:x*K,讓K用位元表示,假設K可表示為一組從位位置n到位位置m的連續的1(n>=m),我們可以用下面兩種不同形式中的一種來計算這些位對乘積的影響
形式A:(x<<n)+(x<<n-1)+...+(x<<m)
形式B:(x<<n+1)-(x<<m)
通過這種形式,不用做乘法,我們就能計算出x*K

p333
代碼移動:這類最佳化包括識別要執行多次但是計算結果不會改變的計算,將計算移動到代碼前面不會多次求值的部分
例如:for (int i=0;i<strlen(s);i++) 這裡應該把strlen(s)提前,用一個變數length儲存
改寫後: int length=strlen(s); for(int i=0;i<length;i++)

p338
迴圈展開:這是一種程式變換,通過增加每次迭代計算的元素的數量,減少迴圈的迭代次數
例如:
for(i=0;i<limit;i+=2)
 acc=(acc OP data[i]) OP data[i+1]
上面就是展開迴圈K=2次,不過最後幾個元素需要單獨處理

p351
兩路並行:對於可結合和可交換的合并運算來說,可以通過將一組合并運算分割成兩個或更多部分,並在最後合并結果來提高效能
例如:
for(i=0;i<limit;i+=2)
{
 acc0=acc0 OP data[i];
 acc1=acc1 OP data[i+1];
}
...
*dest=acc0 OP acc1
上面利用了兩次迴圈展開以及兩路並行

p354
重新結合變換:對代碼做很小的改動,改變合并方式,能夠減少計算中關鍵路徑上操作的數量,通過更好地利用功能單元的流水線能力得到更好的效能
例如:
acc=(acc OP data[i]) OP data[i+1]
acc=acc OP(data[i] OP data[i+1])
僅僅是括弧的差別,但是效能卻大不一樣

p359
關鍵路徑指明了執行某程式所需時間的一個基本的下界,如果某程式存在資料相關鏈,這條鏈上所有延遲之和等於T,那麼這個程式至少需要T個周期才能執行完
同時功能單元的輸送量界限也是程式執行的一個下界,假設一個程式一共需要N個某種運算的計算,而微處理器只有m個能夠執行這個操作的功能單元,並且這些單元的發射時間為i,那麼該程式執行至少需要N*i/m個周期

p369
效能提高技術
A-進階設計 選擇適當的演算法和資料結構
B-基本編碼原則
消除連續的函數調用,盡量將計算移到迴圈外
消除不必要的儲存空間引用,引入臨時變數來儲存中間結構,只有最後計算出的值才儲存在數組或全域變數中
C-低級最佳化
展開迴圈,降低開銷,並且使得進一步的最佳化成為可能
通過使用例如多個累計變數和重新結合等技術,找到方法提高指令級並行
用功能的風格重寫條件操作,使得編譯採用條件資料傳送

p374
Amdahl定律:當我們加快系統一個部分的速度時,對系統整體效能的影響依賴於這個部分有多重要和速度提高了多少
假設系統某個部分需要整個應用程式執行時間的百分比為α,則
加速比 S=1/[(1-α)+α/k] 
所以要想大幅度提高整個系統的速度,必須提高整個系統很大一部分的速度

p402-403
局部性:時間局部性(重複引用),空間局部性(引用附近的一個位置)
步長為1的參考模式為順序參考模式,一般而言,隨著步長的增加,空間局部性下降
評價局部性原則:
重複引用同一個變數的程式有良好的時間局部性
對於步長為k的參考模式的程式,步長越小,空間局部性越好。步長為1有很好的空間局部性,使用大步長的程式空間局部性很差
對於取指令來說,迴圈有好的時間和空間局部性,迴圈迭代次數越多,局部性就越好

p433
使用下列技術利用局部性:
將你的注意力集中在內迴圈上,大部分計算和儲存空間的訪問都發生在那裡
通過按照資料Object Storage Service在儲存空間中的順序、以步長為1的來讀資料,從而使得你程式中的空間局部性最大
一旦從儲存空間中讀入了一個資料對象,僅儘可能多地使用它,從而使得程式中的時間局部性最大

p455
函數和已初始化的全域變數為強符號,未初始化的全域變數為弱符號
unix連結器使用下面規則來處理多重定義的符號:
規則1:不允許有多個強符號
規則2:如果有一個強符號和多個弱符號,那麼就選擇強符號
規則3:如果有多個弱符號,那麼就從這些弱符號中任意選擇一個

p492-522
進程函數
pid_t getpid(void) //返回調用進程的PID
pid_t getppid(void) //返回父進程PID
void exit(int status) //終止進程
pid_t fork(void) //父進程通過此函數建立子進程,父進程中fork返回子進程PID,在子進程中返回0
pid_t waitpid(pid_t pid, int *status, int options)//父進程等待子進程終止或停止 pid>0等待單獨子進程, pid=-1為全部
pid_t wait(int *status) //等待所有子進程
unsigned int sleep(unsigned int secs)//將一個進程掛起一段指定的時間
int pause(void) //讓調用函數休眠,直到該進程收到一個訊號
int execve(const char *filename, const char *agrv[], const char *envp[])//函數載入運行一個新程式
char *getenv(const char *name)//在環境數組中搜尋字串name=value,找到返回一個指向value指標
int setenv(const char *name, const char *newvalue, int overwrite)//用newvalue代替oldvalue
void unsetenv(const char *name)//刪除name=value

訊號
pid_t getpgrp(void)//返回當前進程的進程組ID(每個進程都只屬於一個進程組)
int setpgid(pid_t pid,pid_t,pgid)//改變自己或其它進程的進程組
int kill(pid_t pid, int sig)//終止進程,發送訊號sig給進程pid
unsigned int alarm(unsigned int secs)//進程在secs秒內向自身發sigalam訊號,
sighandler_t signal(int signum, sighandler_t handler)//進程接收訊號
int sigaction(int signum,struct sigaction *act,struct sigaction *oldact)//訊號處理

非本地跳轉
int setjmp(jum_buf env) //在env緩衝區中儲存當前調用環境,為longjmp使用
int sigsetjmp(sigjmp_buf env,int savesigs)//訊號處理使用版本
void longjmp(jum_buf env,int retval)//從env緩衝區中恢複調用環境
void siglongjmp(sigjmp_buf env,int retval)//訊號處理使用版本

p582-585
C程式中常見的與儲存空間有關的錯誤

A-間接引用壞指標
例如:scanf("%d",val)

B-讀未初始化的儲存空間
例如:假設堆儲存空間被初始化為零
int *y=(int *)malloc(n*sizeof(int))
其實這裡並未初始化,使用calloc才能實現

C-允許棧緩衝區溢位
例如:char buf[64]; gets(buf)

D-假設指標和它們指向的對象是相同大小的
例如:int **A=(int **)malloc(n*sizeof(int));  //應該為sizeof(int *)
... A[i]=(int *)malloc(m*sizeof(int))
程式目的建立一個由n個指標組成的數組,每個指標都指向一個包含m個int的數組,但是代碼實際建立的是一個int數組

E-造成錯位錯誤
例如:for(int i=0;i<=m;i++) //考慮=號是否取到

F-引用指標,而不是它指向的對象
例如:binheap[0]=binheap[*size-1];
*size--; //應該為(*size)--

G-誤解指標運算
例如: p為指標
p+=sizeof(int) //應該為p++

H-引用不存在的變數
例如: int val;
return &val; //程式使用一次後,本地變數不再合法

I-引用空閑堆塊中的資料
例如: free(x);
y=x;

J-引起儲存空間泄露
例如: int *x=(int *)malloc (n*sizeof(int)); //使用後沒有free

p597-608
UNIX系統級I/O
int open(char *filename,int flags, mode_t mode)//開啟檔案或建立一個新檔案
int close(int fd)// 關閉檔案
ssize_t read(int fd,void *buf, size_t n)//從描述符fd的檔案位置拷貝最多n個位元組到儲存空間buf
ssize_t write(int fd,const void *buf, size_t n)//從儲存空間拷貝最多n個字元到檔案
int stat(const char *filename, struct stat *buf)//讀取檔案中繼資料
int fstat(int fd,struct stat *buf)//同上
int dup2(int oldfd,int newfd)//I/O重新導向,拷貝描述符表項oldfd到描述符表項newfd

p606
UNIX核心用三個相關的資料結構來表示開啟的檔案
描述符表:每個進程都有自己獨立的描述符表,表項由進程開啟的檔案描述符來索引
檔案表:開啟檔案的集合有一張檔案表來表示,所有的進程共用這張表
v-node表:同檔案表一樣,所有進程共用這張v-node表,每個表項包含stat結構中的大多數資訊

p619-629
UNIX網路編程函數
unsigned long int htonl(unsigned long int hostlong)//將32位整數由主機位元組順序轉換為網路位元組順序
unsigned short int htons(unsigned short int hostshort)//將16位整數由主機位元組順序轉換為網路位元組順序
unsigned long int ntohl(unsigned long int netlong)//將32位整數由網路位元組順序轉換為主機位元組順序
unsigned short int ntohs(unsigned short int netshort)//將16位整數由網路位元組順序轉換為主機位元組順序

int inet_aton(const char *cp,struct in_addr *inp)//將一個點分十進位串cp轉換為一個網路位元組順序的IP地址inp
char *inet_ntoa(struct in_addr_in)//相逆操作,這裡傳遞的是結構本身,不是指向結構的指標

struct hostent *gethostbyname(const char *name)//返回和網域名稱name相關的主機條目
struct hostent *gethostbyaddr(const char *addr, int len,0)//返回和IP地址addr相關聯的主機條目

int socket(int domain, int type, int protocol)//建立一個通訊端描述符
int connect(int sockfd,struct sockaddr *serv_addr, int addrlen)//試圖與通訊端地址為serv_addr的伺服器建立一個網際網路串連
int bind(int sockfd,struct sockaddr *my_addr, int addrlen)//告訴核心將my_addr中的伺服器通訊端地址和通訊端描述符sockfd聯絡起來
int listen(int sockfd, int backlog)//將sockfd從一個主動的通訊端轉化為一個監聽通訊端,接收來自用戶端的串連請求
int accept(int listenfd,struct sockaddr *addr,int *addrlen)//伺服器通過此函數等待用戶端的串連請求

p659-660
線程函數
int pthread_create(pthread_t *tid,pthread_attr_t *attr,func *f,void *arg)//建立一個新線程
pthread_t pthread_self(void) //獲得自己線程的ID
void pthread_exit(void *thread_return) //線程顯示終止,主線程調用等待對等線程終止
int pthread_cancel(pthread_t tid)//終止當前線程
int pthread_join(pthread_t tid,void **thread_return)//等待其他線程終止
int pthread_detach(pthread_t tid)//線程調用此函數分離可結合線程tid
int pthread_once(pthread_once_t *once_control, void(*init_routine)(void))//初始化與線程相關的狀態

 

 

後記:

只能說這本書可能並沒有像吹的那麼牛叉,不知道是不是我能力還未到家的緣故,不過這本書講解的倒是很清晰,其中也有很多編程忠告,在我看來有把電腦群組成原理和作業系統結合的因素,不過顯然無法將那兩大塊都包括其中,只能算是一個鋪墊,後續深入的學習還是應該閱讀更專業的書籍,例如《作業系統-精髓與設計原理》《電腦體繫結構》這些。

 

不知道自己現在看這本書算不算遲了,不過既然能學到東西,我是欣然接受的,多讀書,讀好書,那肯定是程式員必須要學會的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.