linux操作系統-3進程管理(2)

進程調度

進程調度必須防止進程餓死,即低優先級的進程始終得不到運行。

1)進程的切換

來實現多個進程共享CPU。另外,當進程需要睡眠時,也必須切換進程。進程的切換主要包括幾個內容:

1、硬件上下文的切換,主要是寄存器,包括控制寄存器和通用寄存器。

2、任務狀態段的切換

i386提供的一種數據結構。爲了簡化設計,linux內核實現了每一個邏輯CPU一個任務狀態段。內核的堆棧地址是保存在任務狀態段裏,當從用戶空間向內核切換時,硬件自動從任務狀態段中取出內核堆棧地址並初始化堆棧指針寄存器。另一方面,不同的進程使用不同內核堆棧,所以當進程切換時,必須切換任務狀態段中的堆棧指針。

3、內核堆棧

進程的切換一定是發生在內核狀態下,必須切換內核堆棧指針寄存器,是用軟件的方法實現的,linux內核定義一個宏,switch_to,實現內核堆棧指針寄存器的切換。保存當前的堆棧指針寄存器內容,把下一個進程的堆棧指針取出並複製到堆棧指針寄存器。

4)浮點數寄存器

內核在三種情況下會保存浮點數寄存器:任務切換、處理信號、內核自己需要使用浮點數寄存器。

2)進程的調度策略

LINUX進程調度採用策略來區分進程。定義了幾種進程:實時進程、交互式進程、批處理進程。在task_struct中定義了policy字段,指定該進程的調度策略。

Policy可以等於4個值:

SCHED_NORMAL 基於時間片機制。進程具體可以表現爲交互式進程或批處理進程。

SCHED_FIFO 先進先出的實時進程。

SCHED_RR 循環執行的實時進程。

SCHED_BATCH 批處理進程。

調度策略只是比較大概的進程分類,實際調度時必須映射到調度的優先級。LINUX定義了140個優先級。0優先級最高,139最低。總是實時進程優先運行。只有當沒有處於運行狀態的實時進程時,交互式進程和批處理進程纔可以得到調度。

時間片是進程實際被分配到的運行時間,以tick作爲單位。時鐘中斷每發生一次,算作一個tick。每當中斷髮生時,內核就在當前進程的時間片裏減1.如果時間片變成了1,內核就重新調度進程。實際要複雜多了。

實進程包括FIFO和RR兩種。FIFO先進先出,當一個FIFO運行時,如果沒有比它優先級更高的進程可以運行,即使FIFO進程時間片運行完也不會讓CPU,而是再獲得一個新的時間片。RR是循環調度運行的進程,運行完時間片後,需要向同一優先級的其他RR進程讓出CPU。

交互式進程和批處理進程的優先級是動態變化的。內核用一個公式計算出進程新的優先級。公式輸入參數是進程的靜態優先級和最近的過去進程睡眠時間。

3)進程調度的觸發和進程切換時機

進程調度的核心任務由函數schedule處理,主要是選擇並切換到下一個合適的進程。在內核裏,函數scheduler_tick處理由時鐘中斷觸發的調度,是當前進程打標記TIF_NEED_RESCHED.一定要當進程進入合適的時候,就是已經釋放了共享資源後並處於安全的時候,纔可以調用函數schedule做真正的進程調度,否則可能會導致系統的死鎖。

函數try_to_wake_up用於喚醒處理。喚醒優先級高的進程,並且需要在當前CPU運行,就會設置當前進程的TIF_NEED_RESCHED標誌位。另外,創建新的進程和exec時都可能引發進程的調度。

4)運行隊列

運行隊列runqueue是內核支持調度的重要數據結構。每一個邏輯CPU有一個運行隊列,減少CPU之間的競爭。

5)調度域

一般調度時只是從CPU自己的運行隊列選擇一個要運行的進程,必然會出現CPU資源利用不平衡,資源浪費。當一個CPU的運行隊列比較短時,內核就要向其他CPU的運行隊列查詢,如果其他的CPU運行隊列比自己的長,內核會把一些進程從其他CPU運行隊列裏摘除,然後加入到自己的運行隊列,叫做CPU負載平衡。

SMP對稱多處理機:每一個CPU有自己獨立的cache、寄存器和運算單元。

cache設計原理是基於訪問的局部性原理,包括時間局部性原理和空間局部性原理。時間局部性原理是指CPU剛訪問過的數據,在不久的將來還會訪問到;空間局部性原理是指CPU訪問了某一數據後,很可能會訪問這一數據空間上相鄰的數據。cache存放的是內存映像,或者被CPU修改過的數據,以後刷新到內存。

在做CPU負載平衡時,主要的出發點時當進程被移動到另一個CPU的運行隊列後,該進程的運行效率不能降低。主要因素就是cache,或者說訪問數據的速度。根據訪問cache和內存的速度,內核把邏輯CPU劃分成組。首先是分成幾個很小的組,然後幾個小組構成大組,幾個大組又可以構成更大的組,最後一個最大的組包含所有的CPU,這樣一個邏輯CPU同時屬於一個小組,中組,大組,最後一個最大的組,這種分組的設計思想稱作調度域

具體實現上,一個運行隊列有一個調度域鏈表,每個調度域包含幾個調度組,調度組用雙向鏈表連接在一起。

6)搶佔

preempt_disable()禁止搶佔

preempt_enable()打開搶佔

spin_lock()會間接使用禁止函數,所以這樣的函數執行時,禁止搶佔。

kernel態搶佔和用戶態搶佔

7)調度的時間複雜度 O(1)

3.5併發控制原理

1、阻塞操作 當系統需要訪問某些資源時,可能需要阻塞進程等待資源狀態,在這種情況下,某些資源可能需要加鎖保護,以防止阻塞進程被喚醒以前,資源的狀態被破壞。

2、中斷 在內核裏設置一些可搶佔點,把一些關鍵的程序片段標誌成不可搶佔。在這些程序片段裏可以安全的操作內核數據結構而不必擔心被切換到其他進程。

1)原子操作

在很多體系結構下,原子操作需要鎖總線和刷洗處理器的流水線,所以原子操作相對於普通的操作通常開銷還是要更大一些。

2)自旋鎖(轉載)

互斥鎖一樣,一個執行單元要想訪問被自旋鎖保護的共享資源,必須先得到鎖,在訪問完共享資源後,必須釋放鎖。如果在獲取自旋鎖時,沒有任何執行單元保持該鎖,那麼將立即得到鎖;如果在獲取自旋鎖時鎖已經有保持者,那麼獲取鎖操作將自旋在那裏,直到該自旋鎖的保持者釋放了鎖。自旋鎖是一種比較低級的保護數據結構或代碼片段的原始方式,這種鎖可能存在兩個問題:
死鎖。試圖遞歸地獲得自旋鎖必然會引起死鎖:遞歸程序的持有實例在第二個實例循環,以試圖獲得相同自旋鎖時,不會釋放此自旋鎖。在遞歸程序中使用自旋鎖應遵守下列策略:遞歸程序決不能在持有自旋鎖時調用它自己,也決不能在遞歸調用時試圖獲得相同的自旋鎖。此外如果一個進程已經將資源鎖定,那麼,即使其它申請這個資源的進程不停地瘋狂“自旋”,也無法獲得資源,從而進入死循環
過多佔用cpu資源。如果不加限制,由於申請者一直在循環等待,因此自旋鎖在鎖定的時候,如果不成功,不會睡眠,會持續的嘗試,cpu的時候自旋鎖會讓其它process動不了. 因此,一般自旋鎖實現會有一個參數限定最多持續嘗試次數. 超出後, 自旋鎖放棄當前time slice. 等下一次機會。
自旋鎖(spinlock)是一種輕量級的同步原語,它的獲得或等待只需要很少的指令並不需要發生上下文切換。

3)信號量

生產者-消費者問題

變量的操作必須使用特殊的原子操作來完成,即P操作和V操作。P操作是對信號量進行減少,V操作是對信號量進行增加。

信號量的數值(即緩衝區中元素的個數)是不可能爲負數的,所以當信號量爲0時,P操作就會自動進入睡眠狀態,直到隨後的V操作將它喚醒爲止。V操作在任何時候都不會進入到睡眠狀態,原因是信號量的數值可以表示爲足夠大,相當於一個無界緩衝區。在LINUX中P、V操作對應爲DOWN和UP操作。

6)嘗試加鎖(try_lock)

系統在試圖操作關鍵數據結構的時候先測試是否能夠獲得保護這個數據結構的原語,如果不能,系統將轉到其他執行路徑。

3同步原語

原語是由若干條指令組成的,用於完成一定功能的一個過程。由若干個機器指令構成的完成某種特定功能的一段程序,具有不可分割性·即原語的執行必須是連續的,在執行過程中不允許被中斷。

同步原語一般是用來保護數據和資源的,所以在設計同步原語的時候一般把它附在被保護的數據結構上,如果這個功能可以使用原子操作實現,應該優先使用原子操作;佔用時間不長的一些數據結構操作,例如鏈表插入,可以用自旋鎖或者讀寫鎖來保護;佔用時間較長的,或者需要阻塞等待外部資源的,可以使用信號量;還要注意對有些可能被中斷處理程序修改的數據結構的操作需要關中斷來保護(local_irq_disable()和local_irq_enable()),通過設置CPU寄存器來阻塞中斷,當開中斷時,被阻塞的中斷會重新到達,不會丟失。

比較常用的原子操作原語有atomaic_read、atomaic_set、atomic_add、atomic_sub、atomic_add_and_test、atomic_sub_and_test等。內核利用它們來處理簡單數據或者利用它們來構造更復雜的同步原語。

在內核獲得自旋鎖、讀寫鎖、關中斷時,內核搶佔是被關掉的。

信號量

信號量的實現前提是原子操作。由於信號量需要維護一個計數器來記錄信號量的數值以及一些其他需要保護的數據,所以必須使用原子操作來保護他們。在LINUX內核中,信號量關鍵數據的保護是藉助自旋鎖來完成的。

互斥鎖(mutex)是一種新的數據結構,是一個信號量的特例,數值僅爲0或1。

內核中提供互斥鎖的原子操作:mutex_init()、mutex_lock()和mutex_unlock()。

mutex_init()是初始化struct mutex結構,count初始值爲1,表示解鎖狀態。

發佈了27 篇原創文章 · 獲贊 13 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章