實驗
準備環境
進行調試
進程上下文切換相關代碼分析
#define switch_to(prev, next, last)
do {
/*
* Context-switching clobbers all registers, so we clobber
* them explicitly, via unused output variables.
* (EAX and EBP is not listed because EBP is saved/restored
* explicitly for wchan access and EAX is the return value of
* __switch_to())
*/
unsigned long ebx, ecx, edx, esi, edi;
asm volatile("pushfl\n\t" /* save flags */
"pushl %%ebp\n\t" /* save EBP */
"movl %%esp,%[prev_sp]\n\t" /* save ESP */
"movl %[next_sp],%%esp\n\t" /* restore ESP */
"movl $1f,%[prev_ip]\n\t" /* save EIP */
"pushl %[next_ip]\n\t" /* restore EIP */
__switch_canary
"jmp __switch_to\n" /* regparm call */
"1:\t"
"popl %%ebp\n\t" /* restore EBP */
"popfl\n" /* restore flags */
/* output parameters */
: [prev_sp] "=m" (prev->thread.sp),
[prev_ip] "=m" (prev->thread.ip),
"=a" (last),
/* clobbered output registers: */
"=b" (ebx), "=c" (ecx), "=d" (edx),
"=S" (esi), "=D" (edi)
__switch_canary_oparam
/* input parameters: */
: [next_sp] "m" (next->thread.sp),
[next_ip] "m" (next->thread.ip),
/* regparm parameters for __switch_to(): */
[prev] "a" (prev),
[next] "d" (next)
__switch_canary_iparam
: /* reloaded segment registers */
"memory");
} while (0)
分析switch_to中的匯編代碼:
-保存flags
-保存esp
-重置esp
-保存eip
-重置eip
-轉跳至__switch_to
-恢復ebp
-恢復flags
為了控制進程的執行,內核必須有能力掛起正在CPU上執行的進程,并恢復以前掛起的某個進程的執行,這叫做進程切換、任務切換、上下文切換;
掛起正在CPU上執行的進程,與中斷時保存現場是不同的,中斷前后是在同一個進程上下文中,只是由用戶態轉向內核態執行;
進程上下文包含了進程執行需要的所有信息
-用戶地址空間:?包括程序代碼,數據,用戶堆棧等
-控制信息?:進程描述符,內核堆棧等
-硬件上下文(注意中斷也要保存硬件上下文只是保存的方法不同)
分析總結
不同類型的進程有不同的調度需求
-I/O-bound
--頻繁的進行I/O
--通常會花費很多時間等待I/O操作的完成
-CPU-bound
--計算密集型
--需要大量的CPU時間進行運算
需要不同的算法來使得系統運行更加高效,將CPU資源得到最大限度的使用
-批處理進程batch process
--不必與用戶交互,通常在后臺運行
--不必很快響應
--典型的批處理程序:編譯程序、科學計算
-實時進程real-time process
--有實時需求,不應被低優先級的進程阻塞
--響應時間要短、要穩定
--典型的實時進程:視頻、音頻、機械控制等
-交互式進程interactive process
--需要經常與用戶交互,因此要花很多時間等待用戶輸入操作
--響應時間要快,平均延遲要低于50~150ms
--典型交互式程序:shell、文本編輯器、圖形應用程序等
有不同的需求應用存在,就需要有不同的調度應用策略
調度策略是一組規則,決定什么時候以怎樣的方式選擇一個新進程運行
linux的調度基于分時和優先級
linux在不同版本更新,調度策略發生了不同的變化
-linux的進程根據優先級排隊,根據特定的算法計算出進程的優先級,用一個值表示把進程如何適當的分配給CPU
-linux中進程的優先級是動態的,調度程序會根據進程的行為周期性的調整進程的優先級,較長時間未分配到CPU的進程優先級通常增高,已經在CPU上運行了較長時間的進程通常優先級降低
用來配置優先級的調用
-nice
-getpriority/setpriority
-sched_getscheduler/sched_setscheduler
-sched_getparam/sched_setparam
-sched_yield
-sched_get_priority_min/sched_get_priority_max
-sched_rr_get_interval
調度策略只是一種算法,內核中的調度算法相關代碼使用了類似OOD中的策略模式
將調度算法與其他部分解耦合
理解整個系統,在于理解進程調度的時機
schedule函數用于實現調度
-目的:在運行隊列中找到一個進程,把CPU分配給它
-調用方法:-直接調用schedule();-松散調用,根據need_resched標記
進程調度的時機
-中斷處理過程(包括時鐘中斷、I/O中斷、系統調用和異常)中,直接調用schedule(),或者返回用戶態時根據need_resched標記調用schedule();用戶態進程只能被動調度
-內核線程可以直接調用schedule()進行進程切換,也可以在中斷處理過程中進行調度,也就是說內核線程作為一類的特殊的進程可以主動調度,也可以被動調度;內核線程是只有內核態沒有用戶態的特殊進程;內核現成可以主動調度可以被動調動
-用戶態進程無法實現主動調度,僅能通過陷入內核態后的某個時機點進行調度,即在中斷處理過程中進行調度。
王瀟洋
原創作品轉載請注明出處《Linux內核分析》MOOC課程[http://mooc.study.163.com/course/USTC-1000029000]