1. 程式人生 > >Linux進程管理(二)進程的調度與資源限制

Linux進程管理(二)進程的調度與資源限制

失敗 實用程序 代碼 協同 latin 中斷控制 可用 數值 無限

1 進程調度
就緒進程最重要的特征是該進程是非阻塞的。進行用戶交互、大量讀寫文件、響應I/O和網絡事件的進程會花費大量時間來等待資源可用,在相當長的時間內無法轉為就緒狀態(長是相對於指令運行時間而言),因此就緒進程首先應該是非阻塞的。一個就緒進程還必須至少有部分“時間片”(調度器分配給進程的運行時間)。內核用一個就緒隊列維護所有的就緒進程,一旦某進程耗光它的時間片,內核就將其移出隊列,直到所有就緒進程都耗光時間片才考慮將其放回隊列。
多任務操作系統分為兩大類:協同式和搶占式。Linux實現了後一種形式的多任務,調度器可以要求一個進程停止運行,處理器轉而運行另一個進程。這種中止正在運行的進程的行為稱做搶占,類似的,進程在被搶占前所運行的時間稱之為進程時間片(得名於調度器分配給每個就緒進程的一小片時間)。
在協同多任務系統中,一個進程持續運行直到它自發停止。我們稱進程自發停止的行為為讓出。理想情況下,會經常發生進程讓出,但操作系統絕不可強制要求其讓出。因此,一個拙劣或損壞的程序可能運行很長時間,甚至導致整個系統死掉。由於這個原因,現代操作系統幾乎都采用搶占多任務機制,Linux也不例外。
Linux調度算法采用搶占多任務機制,支持多處理器,處理器親和度,非一致內存訪問(NUMA),實時進程和用戶自定義優先級等特性。

時間片。如果時間片太長,進程必須等待很長時間才能運行,這減小了運行的並行性,用戶會察覺到明顯的延遲;相反的,時間片太短,大量時間會花費在進程調度上,程序的時間局部性等也不能得到保證。Linux通過動態分配進程時間片,期望在兩方面都做到最好。

進程不一定要在一次運行中耗光所有時間片。一個被分配100ms時間片的進程,可能運行20ms就因為等待鍵盤輸入等資源而阻塞。此時,調度器就會臨時地把該進程移出就緒隊列;當資源可用後,這個例子中是鍵盤緩沖區不為空,調度器會喚醒進程。進程會繼續運行,耗光剩下的80ms或者又一次阻塞。

持續地消耗所有可用時間片的進程稱為“處理器約束進程”。這類進程渴望CPU時間,消耗掉調度器分配的全部時間。最簡單的例子就是無限循環,其他的例子包括科學計算,數學演算和圖像處理。
多數時間處於等待資源的阻塞狀態的進程稱為“I/O約束進程”。I/O約束進程經常發起和等待文件I/O,阻塞在鍵盤輸入,或者用戶移動鼠標。I/O阻塞程序的例子包括文件實用程序,比如cp或者mv,它們除了請求內核執行I/O操作外,幾乎什麽也不做;還包括GUI應用程序,大多數時候都在等待用戶輸入。

當一個進程耗光時間片的時候,調度器會中止其運行,開始運行一個新的進程。如果沒有其他的就緒進程,內核會給予所有耗光時間片的進程新的時間片,繼續運行。在進程運行時,如果另一個高優先級進程就緒(也許先前此進程阻塞在鍵盤輸入,用戶正好敲入一個單詞),當前運行進程被直接中止,切換到高優先級進程。因此,不會有就緒卻沒有運行的較高優先級進程,系統中的運行進程一定是最高優先級的可運行進程。保證就緒隊列中,最高優先級的運行。

線程是進程中的運行單元,所有的進程都至少有一個線程。每一個線程都獨自占有一個虛擬處理器:獨自的寄存器組,指令計數器和處理器狀態。雖然多數進程都只有一個線程,但是進程實際可以擁有很多線程,每個線程完成不同的任務,但是共享同一地址空間(也就是同樣的動態內存,映射文件,目標代碼等等),打開的文件隊列和其他內核資源。內核把線程簡化為共享資源的進程,也就是說,內核把一個進程中的兩個線程,簡化為共享一系列內核資源(地址空間,打開的文件列表等)的兩個不同進程。

2 讓出處理器
一般來說Unix程序傾向於使用建立在可阻塞文件描述符基礎上的事件驅動機制。當進程IO阻塞時,內核將搶占處理器而不需要該進程顯式讓出處理器。Linux是一個搶占多任務操作系統,很少有合理使用sched_yield()的機會,但是它也提供了一個系統調用來允許進程主動讓出處理器。內核完全有能力作出最優化和最有效率的調度決策,這是因為,內核顯然比一個獨立的應用程序更有資格決定何時搶占哪個進程,協同多任務和搶占多任務兩種不同機制來源於對此的不同理解。
當一個線程試圖請求另一個線程已經擁有的鎖的時候,該線程需要顯式讓出處理器直到鎖可用。在內核不支持用戶空間鎖的時候,這種方法最簡單高效。然而,現代Linux線程實現(the New POSIX Threading Library,or NPTL)迎來了一個基於快速用戶互斥鎖的優化方案,即在內核中提供用戶空間鎖的支持。
2.6版本之後,調用sched_yield()的實際作用就和進程耗光時間片一樣,這不同於早期內核的處理,那時sched_yield()的效果很輕微,而且有乒乓問題。

3 進程優先級
int nice (int inc);
Nice Value [-20, 19],默認值為0,Nice Value越小優先級越高,時間片越長。在運行進程的時候指定,Linux調度器總是優先運行高優先級線程。非Root用戶只能使用正值inc來降低優先級。

int getpriority (int which, int who);
int setpriority (int which, int who, int prio);
which指定作用對象是進程、進程組或者用戶,who指定對應的ID,為0時表示當前作用對象。同nice一樣,非Root用戶只能使用正值inc來降低優先級。

int ioprio_get (int which, int who)
int ioprio_set (int which, int who, int ioprio)
缺省情況下,I/O調度器用進程友好度決定I/O優先級,因此,設置優先級自動改變I/O優先級。作為進程優先級的補充,Linux還允許進程指定I/O優先級,內核I/O調度器總是優先響應來自於高I/O優先級的請求。

4 處理器親和度
對稱多處理SMP,是指在一個計算機上匯集了一組處理器(多CPU),各CPU之間共享內存子系統以及總線結構,由一個操作系統控制。系統將任務隊列對稱地分布於多個CPU之上,從而極大地提高了整個系統的數據處理能力。所有的處理器都可以平等地訪問內存、I/O和外部中斷。在對稱多處理系統中,系統資源被系統中所有CPU共享,工作負載能夠均勻地分配到所有可用處理器之上。一般來講,SMP結構的機器可擴展性較差,很難做到100個以上多處理器,常規的一般是8個到16個。SMP系統對硬件有基本的要求:
1、CPU內部必須內置APIC(Advanced Programmable Interrupt Controllers)單元。Intel多處理規範的核心就是高級可編程中斷控制器(Advanced Programmable Interrupt Controllers--APICs)的使用。CPU通過彼此發送中斷來完成它們之間的通信。通過給中斷附加動作(actions),不同的CPU可以在某種程度上彼此進行控制。每個CPU有自己的APIC(成為那個CPU的本地APIC),並且還有一個I/O APIC來處理由I/O設備引起的中斷,這個I/O APIC是安裝在主板上的,但每個CPU上的APIC則不可或缺,否則將無法處理多CPU之間的中斷協調。
2、相同的產品型號,同樣類型的CPU核心。例如,雖然Athlon和Pentium III各自都內置有APIC單元,想要讓它們一起建立SMP系統是不可能的,當然,即使是Celeron和Pentium III,那樣的可能性也為0,甚至Coppermine核心的Pentium III和Tualatin的Pentium III也不能建立SMP系統--這是因為他們的運行指令不完全相同,APIC中斷協調差異也很大。
3、完全相同的運行頻率。如果要建立雙Pentium III系統,必須兩顆866MHz或者兩顆1000MHz處理器,不可以用一顆866MHz,另一顆1000MHz來組建,否則系統將無法正常點亮。
4、盡可能保持相同的產品序列編號。即使是同樣核心的相同頻率處理器,由於生產批次不同也會造成不可思議的問題。兩個生產批次的CPU作為雙處理器運行的時候,有可能會發生一顆CPU負擔過高,而另一顆負擔很少的情況,無法發揮最大性能,更糟糕的是可能導致死機,因此,應該盡可能選擇同一批生產的處理器來組建SMP系統。

在對稱多處理機(SMP)上,進程調度器必須決定每個CPU上運行哪個進程,因此,必須解決兩個問題:調度器必須充分利用系統的處理器,盡量避免處理器空閑。然而,如果一個進程曾在某一CPU上運行,進程調度器還應該盡量把它放在同一CPU上,因為處理器間的進程遷移會帶來性能損失。最大的性能損失來自於遷移帶來的緩存效應。現代SMP系統的設計中,每個處理器的緩存是各自獨立的,也就是說,處理器並不共享緩存中的數據。
決定何時移動進程來避免不平衡,稱為負載均衡,對SMP機器的性能至關重要。處理器親和度表明一個進程停留在同一處理器上的可能性。術語“軟親和度”(soft affinity)表明調度器持續調度進程到同一處理器上的自然傾向。Linux調度器盡可能地這樣做,只有當負載極端不平衡的時候,才考慮遷移進程。
然而有些時候,用戶或者應用程序需要保證進程和處理器間的綁定,這通常發生在進程非常依賴緩存,期望停留在同一處理器的情況下。術語“硬親和度”(hard affinity)描述了強制內核保證進程到處理器的綁定。進程從父進程繼承處理器親和度;在默認情況下,可能運行在任何CPU上。Linux提供兩個系統調用來獲取和設定進程的硬親和度:
int sched_setaffinity (pid_t pid, size_t setsize, const cpu_set_t set);
int sched_getaffinity (pid_t pid, size_t setsize, const cpu_set_t
set);

5 實時系統
如果一個系統受到操作期限——請求和響應之間的最小量和命令次數的支配,就稱該系統是“實時”的。實時系統分為軟硬實時系統兩大類。硬實時系統對於操作期限要求非常嚴格,超過期限就會產生失敗,後果很嚴重。另一方面,軟實時系統卻不認為超過期限是一個嚴重的失敗。硬實時系統很容易分辨:防抱死系統、軍用武器系統、醫療設備、信號處理都是比較典型的例子。軟實時系統則不太容易分辨,一個比較明顯的例子是視頻處理程序:如果超過了操作時限,用戶會註意到一些質量下降,但是少量的丟幀還是可以忍受的。

Linux對進程的調度行為依賴於進程的調度策略,也稱之為調度類別。Linux提供了兩類實時調度策略作為正常默認策略的補充。頭文件<sched.h>中的預定義宏表示各個策略:分別為SCHED_FIFO,SCHED_RR和SCHED_OTHER。每一個進程都有一個與nice值無關的靜態優先級,對於普通程序,值為0;對於實時程序,它為1到99。Linux調度器始終選擇最高優先級的進程運行(靜態優先級數值最大的進程)。
1,“先進先出”策略:先進先出(FIFO)策略是沒有時間片的非常簡單的實時策略。只要沒有高優
先級進程就緒,FIFO類型進程就會持續運行。特別的,一旦FIFO類進程就緒,它就會直接搶占普通進程。FIFO型進程持續運行直到阻塞或者調用sched_yield(),或者高優先級進程就緒。當FIFO型進程阻塞時,調度器將其移出就緒隊列。
2,輪轉策略,類似於FIFO類型,僅僅引入了處理同等優先級進程的附加規則,以SCHED_RR表示。RR型的時間片僅在相同優先級的進程間相關。
3,普通調度策略,SCHED_OTHER代表標準調度策略,適用於默認的非實時進程。所有這些進程的靜態優先級都為0,因此,任意就緒FIFO或RR形進程都會搶占他們。
4,批調度策略,SCHED_BATCH是批調度或空閑調度的策略,它在某種程度上是實時調度的對立面:這種類型的進程只在系統中沒有其他就緒進程時才會運行,即使那些進程已經耗光時間片。

進程調度相關的系統調用:
int sched_get_priority_min (int policy);
int sched_get_priority_max (int policy);
int sched_getparam (pid_t pid, struct sched_param sp);
int sched_setparam (pid_t pid, const struct sched_param
sp);
int sched_rr_get_interval (pid_t pid, struct timespec *tp);

實時進程樂於看到確定性。在實時計算中,如果給予相同的輸入,一個動作總是在相同的時間內產生相同的結果,我們就說這個動作是確定的。現代計算機可以說是不確定的集合體:多級緩存(命中與否不可預測),多處理器,分頁,交換,和多任務都使估計一個動作需要多長時間變得不可能。確定性實時應用一般會盡量限制不可預測性,和最壞情況下的延時。達到目標有兩種方法。
1,數據故障預測和內存鎖定,對於分頁和交換給實時進程帶來的不確定性,“通過鎖定”或者“硬連接“來將地址空間中的頁提前放入物理內存,阻止其被交換出去。一旦頁被鎖定,內核就不會將起交換出去,任何訪問都不會引起頁錯誤,大多數實時應用都鎖定部分和全部頁面到物理內存。
2,CPU親和度和實時進程,實時應用的第二個難點在於多任務。雖然Linux內核是搶占式的,但是調度器並不總能直接調度另一個進程。有時,當前進程運行在內核中的臨界區,調度器就必須等待它退出臨界區,如果此時有一個實時進程要運行,延時將不可接受,很快就會超出操作期限。因此,多任務和分頁一樣也帶來了相似的不確定。對於多任務的解決方案也一樣:消除它。如果你的系統中有多個處理器,可以指定一個或多個專門用於實時進程。從實際效果上講,你把實時進程和多任務分離開來。一個潛在的對實時進程的優化是為每一個實時進程保留一個處理器,剩下的處理器由其他進程共享。

6資源限制
Linux內核有對進程的資源限制,明確規定了進程可以消耗的內核資源的上限,比如打開文件的數目,內存頁數,未處理的信號等等。限制是強制性的,內核不會允許進程的超過這一硬性限制。結構定義了兩個上限:軟限制和硬限制。內核對進程強制施行軟限制,但進程自身可以修改軟限制,可以是0到硬限制之間的任意值。不具備CAP_SYS_RESOURCE能力的進程(比如,非root進程),只能調低硬限制。非特權程序不能提升硬限制,包括恢復為之前的較高的值;因此,調低硬限制是不可逆的。特權進程則可以設置硬限制為任意合法值。目前Linux提供了15種資源限制:
ResourceLimit SoftLimit HardLimit Comments
RLIMIT_AS RLIM_INFINITY RLIM_INFINITY 進程地址空間上限
RLIMIT_CORE 0 RLIM_INFINITY CoreDump文件最大值
RLIMIT_CPU RLIM_INFINITY RLIM_INFINITY 進程最長CPU時間
RLIMIT_DATA RLIM_INFINITY RLIM_INFINITY 進程數據段和堆的大小
RLIMIT_FSIZE RLIM_INFINITY RLIM_INFINITY 創建的最大文件
RLIMIT_LOCKS RLIM_INFINITY RLIM_INFINITY 文件鎖的最大數量
RLIMIT_MEMLOCK 8Pages 8Pages 內存鎖定最大字節數
RLIMIT_MSGQUEUE 800KB 800KB 消息隊列中分配的最大空間
RLIMIT_NICE 0 0 降底NICE值(提升優先級)的最大值
RLIMIT_NOFILE 1024 1024 可打開的最多文件數
RLIMIT_NPROC 0(Impliesnolimit) 0(Impliesnolimit) 系統任意時刻允許的最多進程數
RLIMIT_RSS RLIM_INFINITY RLIM_INFINITY 進程可以駐留在內存中的最多頁數
RLIMIT_RTPRIO 0 0 最大實時優先級
RLIMIT_SIGPENDING 0 0 用戶消息隊列中最多信號數
RLIMIT_STACK 8MB RLIM_INFINITY 棧的最大字節長度

Linux進程管理(二)進程的調度與資源限制