進程及進程調度

1 .1 進程結構

每一個進程都具備本身的屬性,用一個task_struct數據結構來表示,它包含了進程的詳細信息,主要有進程標識符(PID)、進程所佔的內存區域、相關文件描述符、安全信息、進程環境、信號處理、資源安排、同步處理狀態幾個方面。linux

數組task包含指向系統中全部task_struct結構的指針。建立進程時,Linux將從系統內存中分配一個task_struct結構,並將其加入task數組。操做系統初始化後,創建init進程,它創建一個task_struct數據結構INIT_TASK。當前運行進程的結構用current指針來指示。數組

進程切換包含三個層次:緩存

1)用戶數據的保存 包括正文段、數據段(DATA,BSS)、堆棧段(STACK)、共享內存段(SHARED MEMORY)。安全

2)寄存器數據的保護 包含PC、PSW(處理器狀態字)、SP(棧指針)、PCBP(進程控制塊指針)、FP(指向棧中一個函數的local變量的首地址)、P(指向棧中調用函數的實參位置)、ISP(中斷棧指針),以及其餘通用寄存器等。數據結構

3)系統級的保護 包括proc、u‘虛擬存儲器空間管理表格、中斷處理棧。多線程

(PROC文件系統是Linux中的特殊文件系統,提供給用戶一個能夠了解內核內部工做過程的可讀窗口,在運行時訪問內核內部數據結構、改變內核設置的機制。)併發

 

狀態爲TASK_INTERRUPTBLE或TASK_UNINTERRUPTIBLE的睡眠進程獲得它須要的資源被喚醒,經過schdule()進入TASK_RUNNING狀態。狀態TASK_UNINTERRUPTIBLE的睡眠進程,不能被信號或定時器中斷喚醒,只有它申請的資源有效時才能被喚醒。函數

進程執行do_exit()後進入狀態TASK_ZOMBILE,釋放所申請的資源。spa

 1.2 進程的建立

1.2.1對象緩存的分配

在系統啓動時的啓動內核函數中,有進程管理的初始化函數,其中fork_init函數初始化線程數,分配進程結構的對象緩存。各個進程建立時進程結構對象從這裏分配空間。操作系統

1.2.1系統調用sys_fork

當系統調用sys_fork建立一個進程的時候,它直接調用了實現函數do_fork。do_fork函數拷貝父進程的相關數據,如文件、信號量、內存等。完成進程初始化後,由父進程調用wake_up_process()函數將其喚醒,狀態變爲TASK_RUNNING,掛到就緒隊列,返回子進程的pid。(建立完成的子進程會掛到就緒隊列)

 1.3 內核線程

一個進程能夠擁有多個線程。若是進程運行在SMP機器上,多個CPU執行各個線程,這樣達到最大程度的並行。線程的上下文切換開銷就比進程要小多了,線程共享了進程中除CPU之外的其餘資源。

線程有內核線程、輕量級進程和用戶線程三種,其中內核線程在內核調度,可併發使用多個處理器。用戶線程在用戶空間實現,它減小了上下文切換開銷,它並行處理一個進程中的多個事務。

1)內核線程

內核線程是由內核建立和撤銷的,用來執行一個指定的函數。內核線程共享內核的正文段內核全局數據,但各自具備本身的內核堆棧。它可以被單獨調度,而且使用標準的內核同步機制,能夠被單獨分配到一個處理器上運行。內核線程其實是一個與父進程共享地址空間的進程。

2)輕量級進程

輕量級進程是內核支持的用戶線程。它在一個單獨的進程中提供多線程控制。這些輕量級進程被單獨調度,能夠在多個處理器上運行,每個輕量級進程都被綁定在一個內核線程上。輕量級進程不被獨立調度,而且共享地址空間和進程中的其餘資源,可是每一個輕量級進程都應該有本身的程序計數器、寄存器集合、核心棧和用戶棧。

3)用戶線程

用戶線程是經過線程庫實現的。它們是在沒有內核的參與下進行建立、釋放和管理的。線程庫提供了同步和調度的方法。用戶線程的上下文在沒有內核干預的狀況下保存和恢復。每一個用戶線程能夠有本身的用戶堆棧,一塊用來保存用戶級寄存器上下文,以及入如信號屏蔽等狀態信息的內存區。

內核只調度用戶線程下的進程,這些進程再經過線程庫函數來調度它們的線程。

 1.4 工做隊列

工做隊列接口是用於調度內核工做任務的。每一個工做隊列使用一個專門線程,全部來自運行隊列的工做任務在這個線程中運行,而線程是在進程的上下文中運行的。所以可在適當時間調度此線程來運行工做任務。

 

 1.5 進程調度

在linux中,每個CPU維護一個本身的runqueue結構的就緒隊列。

1.5.1 runqueue結構

runqueue結構是主要的CPU運行隊列數據結構。運行隊列用來按照優先級來管理進程,每一個運行隊列表明必定優先級的進程的鏈表。與工做隊列的區別是:一個工做隊列運用一個線程來運行工做隊列中的各類工做任務,而內核線程實質也是一種進程,工做隊列與運行隊列是徹底不相關的兩個概念。

1.5.2 進程調度初始化

linux的進程有schedule函數執行。它只在內核態運行,任何進程從系統調用返回時會轉入schedule()。大多數中斷服務程序在中斷響應完成後,也會轉入schedule().

1)調度器的初始化

函數sched_init初始化調度器

2)進程調度器相關環境的創建

函數sched_fork爲進程p創建調度器相關環境,進程p是由當前進程用函數fork()新建的進程。

在進程建立fork系統調用中會調用do_fork函數,在do_fork函數中會調用到copy_process函數,而copy_process函數調用到了sched_fork函數,這說明進程建立時,就會創建調度器相關環境。shed_fork函數給進程p分配時間片,打上時間戳。

1.5.3 函數schedule分析

進程的調度有直接啓動調度和被動調度兩種方式,在不一樣的方式下調度執行的步驟是不同的:

1)直接啓動調度

直接啓動調度發生在當前進程因資源而須要進入被阻塞狀態時。調度程序執行的步驟以下:

a 把當前進程放到適當的等待隊列裏;

b 把當前進程的state設爲TASK_INTERRUPTIBEL或者TASK_UNINTERRUPTIBEL;

c 調用schedule(),準備讓新的進程掌握CPU

d 檢查當前進程所需的資源是否可用,若是是,則把當前進程從等待隊列裏刪除。

2)被動調度

經過在當前進程的need_resched設爲1來實現被動調度,每次調入一個用戶態進程以前,這個變量的值都會被檢查,來決定是否調用函數schedule()來實現調度

函數schedule的功能是選擇一個合適的進程在CPU上執行,它的基本流程分爲五個操做步驟:

1)清理當前運行中的進程

2)選擇下一個投入運行的進程

3)設置新進程的運行環境

4)執行進程上下文切換

5)後期整理

 1.6 Linux內核搶佔

內核搶佔就是讓調度程序能儘量多地運行,從而減小了從一個事件發生到調度程序被執行的時間延遲。在當前進程具備被「安全」搶佔條件,而且有一個等待處理的從新調度請求時,內核就調用調度程序來進行進程調度。

內核搶佔要求內核中全部可能爲一個以上進程共享的變量和數據結構都要經過互斥機制加以保護,或者說都要放在臨界區中。在搶佔式內核中,認爲若是內核不是在一箇中斷處理程序中,而且不在spinlock保護的代碼中,就職務能夠「安全」地進行切換。

搶佔式內核實現的原理是在釋放spinlock時,或者當中斷返回時,若是當前執行進程的need_resched被標記,則進行搶佔式調度。

相關文章
相關標籤/搜索