linux swi 内核sp,Linux内核分析课程8_进程调度与进程切换过程
8種機械鍵盤軸體對比
本人程序員,要買一個寫代碼的鍵盤,請問紅軸和茶軸怎么選?
Linux內(nèi)核課第八周作業(yè)。本文在云課堂中實驗樓完成。
原創(chuàng)作品轉(zhuǎn)載請注明出處 《Linux內(nèi)核分析》MOOC課程
1.進程調(diào)度的時機
中斷處理過程(包括時鐘中斷、I/O中斷、系統(tǒng)調(diào)用和異常)中,直接調(diào)用schedule(),或者返回用戶態(tài)時根據(jù)need_resched標記調(diào)用schedule();內(nèi)核線程可以直接調(diào)用schedule()進行進程切換,也可以在中斷處理過程中進行調(diào)度,也就是說內(nèi)核線程作為一類的特殊的進程可以主動調(diào)度,也可以被動調(diào)度;
用戶態(tài)進程無法實現(xiàn)主動調(diào)度,僅能通過陷入內(nèi)核態(tài)后的某個時機點進行調(diào)度,即在中斷處理過程中進行調(diào)度。
2.進程的切換
為了控制進程的執(zhí)行,內(nèi)核必須有能力掛起正在CPU上執(zhí)行的進程,并恢復(fù)以前掛起的某個進程的執(zhí)行,這叫做進程切換、任務(wù)切換、上下文切換;
掛起正在CPU上執(zhí)行的進程,與中斷時保存現(xiàn)場是不同的,中斷前后是在同一個進程上下文中,只是由用戶態(tài)轉(zhuǎn)向內(nèi)核態(tài)執(zhí)行;
進程上下文包含了進程執(zhí)行需要的所有信息:用戶地址空間: 包括程序代碼,數(shù)據(jù),用戶堆棧等
控制信息: 進程描述符,內(nèi)核堆棧等
硬件上下文(注意中斷也要保存硬件上下文只是保存的方法不同)
3.具體進程切換的代碼分析
schedule()函數(shù)選擇一個新的進程來運行,并調(diào)用context_switch進行上下文的切換,這個宏調(diào)用switch_to來進行關(guān)鍵上下文切換:主要調(diào)用過程:next = pick_next_task(rq, prev);//進程調(diào)度算法都封裝這個函數(shù)內(nèi)部
context_switch(rq, prev, next);//進程上下文切換
switch_to利用了prev和next兩個參數(shù):prev指向當(dāng)前進程,next指向被調(diào)度的進程
1)schedule()函數(shù)
首先,切換時候,調(diào)用call schedule();來執(zhí)行schedule()函數(shù),如下圖所示:
使用struct task_struct *tsk = current; 來獲取當(dāng)前進程;sched_submit_work(tsk); 避免死鎖;最后調(diào)用_schedule()來處理切換過程
2)_schedule()函數(shù)
其中 need_resched:為切換前的變量準備:preempt_disable();//禁止內(nèi)核搶占;
cpu = smp_processor_id(); //獲取當(dāng)前CPU
rq = cpu_rq(cpu); //獲取該CPU維護的運行隊列(run queue)
rcu_note_context_switch(cpu); //更新全局狀態(tài),標識當(dāng)前CPU發(fā)生上下文的切換
prev = rq->curr; //運行隊列中的curr指針賦予prev。
其中的next=pick_next_task(rq, prev)來確定使用哪一種進程調(diào)度的策略,但總是選擇了下一個進程來進行切換,即根據(jù)調(diào)度策略選擇一個優(yōu)先級最高的任務(wù)將其定為下一個進程,最后都是調(diào)用context_switch來進行進程上下文的切換過程.
3)context_switch函數(shù)解析
其中prepare_task_switch()函數(shù)是完成切換前的準備工作;接著后面判斷當(dāng)前進程是不是內(nèi)核線程,如果是內(nèi)核線程,則不需要切換上下文
接著調(diào)用switch_mm(),把虛擬內(nèi)存從一個進程映射切換到新進程中
調(diào)用switch_to(),從上一個進程的處理器狀態(tài)切換到新進程的處理器狀態(tài)。這包括保存、恢復(fù)棧信息和寄存器信息如果next是內(nèi)核線程,則線程使用prev所使用的地址空;schedule( )函數(shù)把該線程設(shè)置為懶惰TLB模式
事實上,每個內(nèi)核線程并不擁有自己的頁表集(task_struct->mm = NULL);更確切地說,它使用一個普通進程的頁表集。不過,沒有必要使一個用戶態(tài)線性地址對應(yīng)的TLB表項無效,因為內(nèi)核線程不訪問用戶態(tài)地址空間。
如果next是一個普通進程,schedule( )函數(shù)用next的地址空間替換prev的地址空間
|————————————–|
| } else |
| switch_mm(oldmm, mm, next); |
|————————————–|
如果prev是內(nèi)核線程或正在退出的進程,context_switch()函數(shù)就把指向prev內(nèi)存描述符的指針保存到運行隊列的prev_mm字段中,然后重新設(shè)置prev->active_mm
context_switch()最后調(diào)用switch_to()執(zhí)行prev和next之間的進程切換了
|———————————-|
| switch_to(prev, next, prev); |
|———————————-|
return prev;
}
4)switch_to()函數(shù)解析
switch_to(prev, next, prev):切換堆棧和寄存器的狀態(tài).
switch_to是一個宏定義,完成的工作主要是:
(1)保存當(dāng)前進程的flags狀態(tài)和當(dāng)前進程的ebp1
2"pushflnt"
"pushl %%ebpnt"/* save EBP */
(2)完成內(nèi)核堆在esp的切換1
2"movl %%esp,%[prev_sp]nt" /* save ESP */
"movl %[next_sp],%%espnt" /* restore ESP */
進程切換的時候,要修改堆棧,eip等數(shù)據(jù).在switch_to中完成了這個工作。
(3)保存eip的值1
2"movl $1f,%[prev_ip]nt" /* save EIP */
"pushl %[next_ip]nt" /* restore EIP */
將標號1:的地址保存到prev->thread.ip中,然后下一次該進程被調(diào)用的時候,就從1的位置開始執(zhí)行。注明:如果之前next也被switch_to出去過,那么next->thread.ip里存的就是下面這個1f的標號,但如果next進程剛剛被創(chuàng)建,之前沒有被switch_to出去過,那么next->thread.ip里存的將是ret_ftom_fork,即進程剛剛被fork后執(zhí)行exec.
(4)jmp __switch_to
讓參數(shù)不壓入堆棧,而是使用寄存器傳值,來調(diào)用__switch_to eax存放prev,edx存放next。
二.gdb跟蹤schedule函數(shù)
小結(jié):整個schedule的執(zhí)行過程如下圖所示
|———————————-|
schedule
sched_submit_work(tsk)
_schedule()
pick_next_task
context_switch(rq,prev,next)
prepare_task_switch
判斷是不是內(nèi)核線程
switch_mm
switch_to
_switch_to
finish_task_switch
總結(jié)
以上是生活随笔為你收集整理的linux swi 内核sp,Linux内核分析课程8_进程调度与进程切换过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux需要的GLIBCXX版本,GC
- 下一篇: sudu在linux的命令,Linux的