1. 程式人生 > >讀書筆記--《程式設計師的自我修養》第一章:簡介

讀書筆記--《程式設計師的自我修養》第一章:簡介

一、計算機軟體體系結構

二、作業系統和CPU
1、作業系統的功能:提供抽象的介面,管理硬體資源。

2、充分利用CPU的方法
(1)多道程式利用
編寫一個監控程式,當程式暫時無法使用CPU時,監控程式就把另外的正在等待CPU資源的程式啟動,充分利用CPU。
缺點:程式排程策略太粗糙
(2)分時系統
每個程式執行一段時間後都主動讓出CPU給其他程式,使得一段時間內每個程式都有機會執行一小段時間。尤其適合互動式的任務。
(3)多工系統
作業系統接管所有的硬體資源,並且本身執行在一個受硬體保護的級別。所有的應用程式都以程序的方式執行在比作業系統許可權更低的級別。CPU由作業系統統一進行分配,每個程序根據程序優先順序的高低都有機會得到CPU,但是如果執行時間超出了一定的時間,作業系統會暫停該程序,將CPU資源分配給其他等待執行的程序。這種CPU分配方式稱為搶佔式,OS可以強制剝奪CPU資源並且分配給它認為目前最需要的程序。如果OS分配給每個程序的時間都很短,即CPU在多個程序間快速切換,從而造成很多程序都同時執行的假象。

三、裝置驅動
1、驅動程式可以看作是OS的一部分,和OS核心執行在特權級,但它又與OS核心之間有一定的獨立性,使得驅動程式有比較好的靈活性。

2、linux檔案系統ext2、ext3

3、硬碟的基本儲存單位是扇區,每個扇區一般為512位元組。每個硬碟往往有多個碟片,每個碟片分兩面,每面按照同心圓劃分為若干個磁軌,每個磁軌劃分為若干個扇區。比如一個硬碟有2個碟片,每個碟片有65536個磁軌,每個磁軌有1024個扇區,則該硬碟容量為2*2*65536*1024*512=128GB。由於每個碟片上同心圓周長不一樣,如果按照每個磁軌都擁有相同數量的扇區,那麼靠近盤面外圍的磁軌密度肯定比內圈更加稀疏,這樣是比較浪費空間的。但是如果不同的磁軌扇區數量不同,計算會比較複雜。為了遮蔽這些複雜的硬體細節,現代硬碟普遍使用一種叫做LBA的方式,即整個硬碟中所有的扇區從0開始編號,一直到最後一個扇區,這個扇區叫做邏輯扇區號。

四、記憶體分配
1、記憶體分配問題
(1)地址空間不隔離
所有程式直接訪問實體地址,程式使用的記憶體空間不是隔離的,惡意程式會很容易改寫其他程式的記憶體資料,達到破壞
(2)記憶體使用效率低
執行一個程式時,需要將整個程式裝入記憶體中然後開始執行。如果要突然執行一個程式,但是記憶體空間已經不夠了,這時候我們就需要將其他程式重新裝入磁碟,等到用的時候再讀回來。這樣大量資料換入換出,導致效率低下。
(3)程式執行地址不確定
因為程式每次需要裝入執行時,我們都需要給它從記憶體中分配一塊足夠大的空閒區域,這個空虛區域的位置是不確定的,給程式的編寫造成了一定的麻煩。這將涉及到重定位問題

2、解決問題
(1)針對這個問題,引入虛擬地址的概念。
(2)分段
這裡寫圖片描述
分段有效解決了(1)(3)兩個問題,因為它不需要關心分配到實體地址的哪一個區域,只需要按照虛擬地址0x,,,~0x…來編寫程式即可。但是其無法解決效率低的問題,因為如果記憶體不足,被換入到磁碟的都是整個程式,這樣勢必會造成大量的磁碟訪問操作,從而嚴重影響速度。事實上,根據程式的區域性性原理,當一個程式在執行時,在某個時間段內,它只是頻繁地用到了一小部分資料,也就是說,程式地很多資料其實在一個時間段內都是不會被用到的!因此人們想到了粒度更小的記憶體分割和對映方法,使得程式的區域性性原理得到充分利用,大大提高了記憶體的使用率。這種方法就是分頁。

(3)分頁
每一頁的大小由硬體決定,或硬體支援多種大小的頁,由OS選擇決定頁的大小。目前幾乎所有的PC上的OS都使用4KB大小的頁。
虛擬儲存的實現需要依靠硬體的支援,對於不同的CPU來說是不同的。但是幾戶所有的硬體都採用一個叫做MMU的部件來進行頁對映。
這裡寫圖片描述
在頁對映下,CPU發出的是virtual address,即我們的程式看到的是虛擬地址,經過MMU轉換以後就變成了physical address。一般MMU整合在CPU內部了,不會以獨立的部件存在。

五、執行緒
1、執行緒,或輕量級程序(LWP)是程式執行流的最小單元。通常,一個程序由一個到多個執行緒組成,各個執行緒之間共享程式的記憶體空間(包括程式碼段、資料段和堆段)及一些程序級的資源(如開啟檔案和訊號)。

2、執行緒的訪問許可權
執行緒的訪問非常自由,它可以訪問程序記憶體裡的所有資料,甚至包括其他執行緒的堆疊(如果它知道其他執行緒的堆疊地址,那麼這就是很少見的情況),但實際中執行緒也有自己的私有儲存空間。包括棧、執行緒區域性儲存(TLS)、暫存器(是執行流的基本資料,因此為執行緒私有)
這裡寫圖片描述

3、執行緒的排程和優先順序
當執行緒數量小於處理器數量時,執行緒的併發是真正的併發,不同的執行緒執行在不同的處理器上,彼此不相干。但是對於執行緒數量大於處理器數量的情況,執行緒的併發會受到一些阻礙,因為此時至少有一個處理器會執行多個執行緒。這樣也給不斷在處理器上切換不同的執行緒的行為稱之為執行緒排程。
這裡寫圖片描述
我們一般把頻繁等待的執行緒稱之為IO密集型執行緒,把很少等待的執行緒稱為CPU密集型執行緒。
在優先順序排程下,存在一種餓死的現象。執行緒的優先順序改變一般有三種方式。
(1)使用者指定優先順序
(2)根據進入等待狀態的頻繁程度提升或降低優先順序
(3)長時間得不到執行而被提升優先順序

4、Linux多執行緒
Linux核心中並不存在真正意義上的執行緒概念。Linux將所有的執行實體都稱為任務。每一個任務概念上都類似於一個單執行緒的程序,具有記憶體空間、執行實體、檔案資源等。不過,Linux下不同任務之間可以選擇共享記憶體空間,因此共享了同一個記憶體空間的多個任務構成了一個程序,這些任務也就成了這個程序裡的執行緒。
在Linux下,用以下方法可以建立一個新任務。
這裡寫圖片描述
fork函式產生一個和當前程序完全一樣的新程序,並和當前程序一樣從fork函式裡返回。其中本任務的fork將返回新任務的pid,而新任務的fork將返回0。
這裡寫圖片描述

5、執行緒安全
為了避免多個執行緒同時讀寫同一資料而產生不可預料的後果,需要將各個執行緒對同一個資料的訪問同步。同步既是指在一個執行緒訪問資料未結束的時候,其他執行緒不得對同一個資料進行訪問。這樣對資料的訪問被原子化了。
同步最常見的方法是使用鎖。
(1)二元訊號量
是一種最簡單的鎖,只有兩種狀態:佔用和非佔用。它適合只能被唯一一個執行緒獨佔的資源。當其處於非佔用狀態時,第一個試圖獲取該二元訊號量的執行緒會獲得該鎖,並將其置為佔用狀態,此後其他所有試圖獲取該二元訊號量的執行緒將會等待,直到該鎖被釋放。

(2)互斥量
與二元訊號量類似,資源僅同時允許一個執行緒訪問,但區別是互斥量要求哪個執行緒獲取了互斥量,哪個執行緒就要負責釋放這個鎖,而訊號量不是。

(3)臨界區
是比互斥量更加嚴格的同步手段。它和互斥量和訊號量的區別在於,互斥量和訊號量在系統的任何程序裡都是可見的,即,一個程序建立了一個互斥量或訊號量,另一個程序試圖獲取該鎖是合法的。然而,臨界區的作用範圍僅限於本程序,其他程序無法獲得該鎖。

(4)讀寫鎖
適於讀取頻繁偶爾寫入的情況。對於同一個鎖,讀寫鎖有兩種獲取方式,共享的或獨佔的。
當鎖處於自由的狀態時,試圖以任何一種方式獲取鎖都能成功,並將鎖置於對應的狀態;
當鎖處於共享的狀態時,其他執行緒以共享的方式獲取鎖仍然會成功,此時這個鎖分配給了多個執行緒。然而,當其他執行緒以獨佔的方式獲取時,它將必須等待鎖被所有的執行緒釋放。
當鎖處於獨佔狀態時,將阻止任何其他執行緒獲取該鎖,不論他們試圖以哪種方式獲取。
這裡寫圖片描述

(5)條件變數
作為一種同步手段,作用類似於一個柵欄。對於條件變數,執行緒可以有兩種操作,首先執行緒可以等待條件變數,一個條件變數可以被多個執行緒等待。其次執行緒可以喚醒條件變數,此時某個或所有等待條件變數的執行緒都會被喚醒並繼續支援。

6、多執行緒內部情況
(1)一對一模型
一個使用者使用的執行緒唯一對應一個核心使用的執行緒(反過來不一定)。這時,執行緒之間的併發是真正的併發。

(2)多對一模型
多個使用者執行緒對映到一個核心執行緒上,執行緒之間的切換由使用者態的程式碼來進行。因此相對一對一模型,多對一模型的執行緒切換要快速的多。缺點:如果也給使用者執行緒阻塞,那麼所有的執行緒都無法執行,因此此時核心裡的執行緒也阻塞了。

(3)多對多模型
多對多模型結合了多對一模型和一對一模型的特點,將多個使用者執行緒對映到少數但不止一個核心執行緒上。在此模型中,一個使用者執行緒阻塞並不會使得所有的使用者執行緒阻塞,因為此時還有別的執行緒可以被排程來執行。另外,多對多模型對使用者執行緒的數量也沒有限制,在多處理器系統上,多對多模型的執行緒也能得到一定的效能提升,不過提升的幅度不如一對一模型高。