1. 程式人生 > >程式設計師的自我修養-第一章 筆記

程式設計師的自我修養-第一章 筆記

1.計算機硬體的三個部件最為重要:中央處理器CPU,記憶體和IO控制晶片。

2.主機板上北橋晶片處理高速裝置。南橋晶片處理低速裝置,然後彙總後連線到北橋上。

3.多個處理之間共享比較昂貴的緩衝部件,只保留報個核心,並且以一個處理器的外包裝出售,售價比單核心的處理器只貴了一點,這就是多核處理器(Multi-core Processor)的基本想法。

4.作業系統核心層對於硬體層來說是硬體介面的使用者,而硬體是介面的定義者,硬體的介面定義決定了作業系統核心,具體來講就是驅動程式如何操作硬體,如何與硬體進行通訊。這種介面往往被叫做硬體規格(HardWare Specification),硬體的生產廠商負責提供硬體規格,作業系統和驅動程式的開發者通過閱讀硬體規格所規定的各種硬體程式設計介面標準來編寫作業系統和驅動程式。

5.早期的計算機中,程式是直接執行在實體記憶體上的,多個程式執行時也都被分配在實體記憶體上,這樣的分配策略引發了3個重要問題:程式之間的地址空間不隔離,記憶體使用效率低,程式執行的地址不確定。解決這幾個問題的思路就是增加中間層,即使用一種間接的地址訪問方法。整個想法是這樣的,我們把程式給出的地址看作是一種虛擬地址(Virtual Address),然後通過某些對映的方法,將這個虛擬地址轉換成實際的實體地址。這樣,只要我們能夠妥善地控制這個虛擬地址到實體地址的對映過程,就可以保證任意一個程式所能夠訪問的實體記憶體區域跟另外一個程式相互不重疊,以達到地址空間隔離的效果,同時也可以做到每個程式的地址是相同的。而記憶體使用效率低這個問題人,們很自然地想到了更小粒度的記憶體分割和對映的方法,使得程式的區域性性原理得到充分的利用,大大提高了記憶體的使用率。這種方法就是分頁(Paging)。

6.虛擬儲存的實現需要依靠硬體的支援,對於不同的CPU來說是不同的,但是幾乎所有的硬體都採用一個叫做MMU(Memory Management Unit)的部件來進行頁對映。一般MMU都整合在CPU內部了,不會以獨立的部件存在。

7.執行緒(Thread),有時被稱為輕量級程序(Lightweight Process,LWP),是程式執行流的最小單元。一個標準的執行緒由執行緒ID、當前指令指標(PC),暫存器集合和堆疊組成。通常意義上,一個程序由一個到多個執行緒組成。各個執行緒之間共享程式的記憶體空間(包括程式碼段,資料段,堆等)及一些程序級的資源(如開啟檔案和訊號)。

8.一般把頻繁等待的執行緒稱之為IO密集型執行緒(IO Bound Thread),把很少等待的執行緒成為CPU密集型執行緒(CPU Bound Thread)。IO密集型執行緒總是比CPU密集型執行緒容易得到優先順序的提升。線上程優先順序排程下,存在一種餓死(Starvation)的現象,一個執行緒被餓死,是說它的優先順序較低,在它執行之前,總是有較高優先順序的執行緒試圖執行,因此這個低優先順序執行緒始終無法執行。當一個CPU密集型的執行緒獲得較高的優先順序時,許多低優先順序的執行緒很可能餓死。而一個高優先順序的IO密集型執行緒由於大部分時間都處於等待狀態,因此相對不容易造成其他執行緒餓死。為了避免餓死現象,排程系統常常會逐步提升那些等待了過長時間的得不到執行的執行緒的優先順序。在這樣的手段下,一個執行緒只要等待足夠長的時間,其優先順序一定會提高到足夠讓它執行的程式。

9.fork產生新任務的速度非常快,因為fork並不複製原任務的記憶體空間,而是和原任務一起共享一個寫時複製(Copy On Write,COW)的記憶體空間。所謂寫時複製,指的是兩個任務可以同時自由地讀取記憶體,單任意一個任務試圖對記憶體進行修改時,記憶體就會複製一份提供給修改方單獨使用,以免影響到其他的任務使用。

10.互斥量和二元訊號量很類似,資源僅同時允許一個執行緒訪問,但和訊號量不同的是,訊號量在整個系統可以被任意執行緒獲取並釋放,也就是說,同一個訊號量可以被系統中的一個執行緒獲取之後由另一個執行緒釋放。而互斥量則要求那個執行緒獲取了互斥量,哪個執行緒就要負責釋放這個鎖,其他執行緒越俎代庖去釋放互斥量是無效的。

11.臨界區是比互斥量更加嚴格的同步手段。互斥量和訊號量在系統的任何程序都是可見的,也就是說,一個程序建立了一個互斥量或訊號量,另一個程序試圖去獲取該鎖是合法的。然而,臨界區的作用範圍僅限於本程序,其他的程序無法獲取該鎖,除此之外,臨界區具有和互斥量相同的性質。

12.一個函式要成為可重入的,必須具有如下幾個特點:
  1.不使用任何(區域性)靜態或全域性的非const變數;
  2.不返回任何(區域性)靜態或全域性的非const變數的指標;
  3.僅依賴於呼叫方提供的引數;
  4.不依賴任何單個資源的鎖(mutex等);
  5.不呼叫任何不可重入的函式。
  可重入是併發安全的強力保障,一個可重入的函式可以在多執行緒環境下放心使用。

13.volatile關鍵字試圖阻止過度優化,volatile基本可以做到兩件事情:
  1.阻止編譯器為了提高速度將一個變數快取到暫存器內而不寫回;
  2.阻止編譯器調整操作volatile變數的指令順序(即使volatile能夠阻止編譯器調整順序,也無法阻止CPU動態排程換序);

14.CPU的亂序執行能力讓我們對多執行緒的安全保障的努力變得異常困難。因此要保證執行緒安全,阻止CPU換序是必需的。遺憾的是,現在並不存在可移植的阻止換序的方法。通常情況下是呼叫CPU提供的一條指令,這條指令常常被稱為barrier。一條barrier指令會阻止CPU將該指令之前的指令交換到barrier之後,反之亦然。換句話說,barrier指令的作用類似於一個攔水壩,阻止換序“穿透”這個大壩。
  關於barrier()巨集實際上也是優化屏障:

  #define barrier() __asm__ volatile (”lwsync”)

  CPU越過記憶體屏障後,將重新整理自己對儲存器的緩衝狀態。這條語句實際上不生成任何程式碼,但可使gcc在barrier()之後重新整理暫存器對變數的分配。

  1.set_mb(),mb(),barrier()函式追蹤到底,就是__asm__ __volatile__("":::"memory"),而這行程式碼就是記憶體屏障;
  2.__asm__用於指示編譯器在此插入彙編語句;
  3.__volatile__用於告訴編譯器,嚴禁將此處的彙編語句與其它的語句重組合優化。