Linux進程調度
1、程式VS進程
程式是存放在磁碟上的一序列代碼和資料的可執行映像,是一個靜止的實體。
進程是一個執行中的程式,是動態實體。
2、Linux進程的四大要素
<1>有一段供進程執行的程式,該程式可以被多個進程執行。
<2>:有進程專用的核心空間堆棧。
<3>:進程式控制制快(task_struct:有了這個資料結構,進程才能成為核心調度的一個基本單位接受核心的調度。
<4>:獨立的使用者空間
進程:有獨立的進程空間
線程:只有前三條,沒有第四條。
核心線程:完全沒有使用者空間。
使用者線程:共用使用者空間。
3、Linux進程分類:
<1>:互動式進程:這些進程經常和使用者發生互動,所以花費一些時間等待使用者的操作。當有輸入時,進程必須很快的啟用。通常,要求延遲在50-150毫秒。典型的互動式進程有:控制台命令,文字編輯器,圖形應用程式。
<2>:批處理進程(Batch Process):不需要使用者互動,一般在後台運行。所以不需要非常快的反應,他們經常被調度期限制。典型的批處理進程:編譯器,資料庫搜尋引擎和科學計算。
<3>:即時進程:對調度有非常嚴格的要求,這種類型的進程不能被低優先順序進程阻塞,並且在很短的時間內做出反應。典型的即時進程:音視頻應用程式,機器人控制等。
批處理進程可能與I/O或者CPU有關,但是即時進程完全通過Linux的調度演算法識別。
其實互動式進程和批處理進程很難區別。
4:Linux進程優先順序
<1>、靜態優先順序(priority): 被稱為“靜態”是因為它不隨時間而改變,只能由使用者進行修改。它指明了在被迫和其它進程競爭CPU之前該進程所應該被允許的時間片的最大值(20)。
每個普通進程都一個靜態優先順序,核心為其分配的優先順序數為:100(高優先順序)-139(低優先順序)。數值越大,優先順序越低。新建立的進程一般繼承父進程的優先順序,但是使用者可以通過給nice()函數傳遞“nice value“或者setpriority()改變優先順序。
<2>:、動態優先順序(counter): counter 即系統為每個進程運行而分配的時間片,Linux 兼用它來表示進程的動態優先順序。只要進程擁有CPU,它就隨著時間不斷減小;當它為0 時,標記進程重新調度。它指明了在目前時間片中所剩餘的時間量(最初為20)
事實上,在進程在調度的時候,調度器只察看動態優先順序,其值為100-139。通過下面的公式可以根據靜態優先計算出相應的動態優先順序。
Dynamicy priority = max (100, min (static priority - bonus + 5, 139))
Bonus:0-10,比5小,降低動態優先順序,反之,可以提高動態優先順序。Bonus和進程的平均睡眠時間有關。
<3>、 即時優先順序(rt_priority):值為1000。Linux把即時優先順序與counter值相加作為即時進程的優先權值。較高權值的進程總是優先於較低權值的進程,如果一個進程不是即時進程,
其優先權就遠小於1000,所以即時進程總是優先。
<4>、Base time quantum:是由靜態優先順序決定,當進程耗盡當前Base time quantum,kernel會重新分配一個Base time quantum給它。靜態優先順序和Base time quantum的關係為:
(1) 當靜態優先順序小於120
Base time quantum(in millisecond)= (140 – static priority) * 20
(2) 當靜態優先順序大於等於120
Base time quantum(in millisecond)= (140 – static priority) * 5
5、Linux 進程的調度演算法
<1>、時間片輪轉調度演算法(round-robin):SCHED_RR,用於即時進程。系統使每個進程依次地按時間片輪流執行的方式。
<2>、優先權調度演算法:SCHED_NORMAL,用於非即時進程。系統選擇運行隊列中優先順序最高的進程運行。Linux 採用搶佔式的優級演算法,即系統中當前啟動並執行進程永遠是可運行進程中優先權最高的那個。
<3>、FIFO(先進先出) 調度演算法:SCHED_FIFO,用於即時進程。
6、Linux 進程的調度時機schedule()
方式:主動式和被動式
主動式:在核心中直接調用schedule()
current->state=TASK_INTERRUPTIBLE
TASK_RUNNING
被動式:使用者搶佔——從系統調用或者中斷處理常式返回使用者空間
核心搶佔——在不支援核心搶佔的系統中,進程一旦運行於核心空間就可以一直運行,直到它主動放棄或者時間片耗盡。
在支援核心搶佔的系統中,更高優先順序的進程可以搶佔正在核心空間啟動並執行低優先順序進程。
有些特例是不允許搶佔的。
<1>、核心正在中斷處理
<2>、核心正在中斷內容相關的Bottom Half處理
<3>、進程正持有Spinlock自旋鎖
<4>、核心正在執行發送器schedule()
7、程式碼分析
<1>、2.6 版的核心仍然用 task_struct 來表徵進程,儘管對線程進行了最佳化,但線程的核心表示仍然與進程相同。隨著調度器的改進,task_struct 的內容也有了改進,互動式進程優先支援、核心搶佔支援等新特性,在task_struct 中都有所體現。在 task_struct 中,有的屬性是新增加的,有的屬性的值的含義發生了變化,而有的屬性僅僅是改了一下名字。可稱為進程式控制制塊(TCB),主要包含進程標識符、優先順序、堆棧空間、進程狀態
task_struct原始碼定義在kernel/include/Linux/sched.h中
struct task_struct { volatile longstate; /* -1 unrunnable, 0 runnable, >0 stopped */ structthread_info *thread_info; atomic_tusage; unsigned longflags; /* per process flags, defined below */ unsigned longptrace; int lock_depth; /* Lock depth */ int prio, static_prio; structlist_headrun_list; prio_array_t *array; unsigned long sleep_avg; long interactive_credit; unsigned long longtimestamp; int activated; unsigned long policy; cpumask_t cpus_allowed; unsigned int time_slice, first_time_slice; structlist_headtasks; /* * ptrace_list/ptrace_children formsthe list of my children * that were stolen by a ptracer. */ structlist_head ptrace_children; structlist_head ptrace_list; structmm_struct *mm, *active_mm;};
<2>、核心結構task_struct中的state
進程的狀態仍然用 state 表示
#define TASK_RUNNING 0#define TASK_INTERRUPTIBLE 1#define TASK_UNINTERRUPTIBLE 2#define TASK_STOPPED 4#define TASK_ZOMBIE 8#define TASK_DEAD 16
2.6新增加了兩種狀態:TRACED、DEAD。
新增加的TASK_DEAD指的是已經退出且不需要父進程來回收的進程。TASK_TRACED供調試使用。
原有的幾個state:
TASK_ZOMBIE一個已經終止的但仍保留有任務的進程(已經死了,戶口未登出)。
TASK_RUNNING就緒態(準確的說你應該是task_runable)
TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE不同深度的睡眠態
TASK_STOPPED描述一個已經停止的進程,當進程收到一個特殊訊號或被時候ptrace系統調用的進程監控,並將監控權交給監控進程。Linux2.4版,核心在建立進程是,為每個進程配兩個連續的物理頁面(8KB)它的頂端(低地址部分)用來儲存進程的task_struct結構(約1KB),剩下的約7KB就是進程的系統空間堆棧,核心可以通過棧寄存器指標ESP快速地方位該進程。
<3>thread_info
在linux2.6中。這兩個頁面頂端存放的不再是進程的整個task_struct結構,而是task_struct中的thread_info,task_struct的大部分資訊儲存在棧外,通過的task指標可以方便地訪問到。
thread_info是描述一個描述任務的重要結構體,下面將看到thread_info的資料結構定義(/include/asm-386/thread_.h)
struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thead 0-0xFFFFFFFF for kernel-thread */ struct restart_block restart_block; unsigned long previous_esp; /* ESP of the previous stack in case of nested (IRQ) stacks */ __u8 supervisor_stack[0]; };
一些主要的環境資訊如下:
task指標指向其對應的任務控制塊
preempt_count是用來表示核心能否被搶佔的使能成員,如果它大於0.表示核心不能被搶佔:如果等於0,則表示核心處於安全狀態(即沒有加鎖),可以搶佔。
Flags裡面有一個TIF_NEED_RESCHED(調度標誌)位,如果此標誌位為1.則表示應該儘快啟動調度器。
8、調度步驟
Schedule()函數工作流程:
<1>、清理當前運行中的進程
<2>、選擇下一個要啟動並執行進程
<3>、設定新進程的運行環境
<4>、進程環境切換