我今天分析一下進程的上下文切換,也就是進程調度時,怎麼由當前進程切換到另外一個進程的。linux
一、概述算法
進程調度的時機,也就是進程是在啥時候切換,觸發因數是什麼。函數
中斷髮生時,進入中斷處理中斷服務程序——好比咱們前面講的系統調用,會直接調用schedule(),或者返回用戶態時根據need_resched標誌調用scheduleurl
內核線程能夠直接調用schedule(),從而主動調度。.net
用戶態進程沒法調用到內存函數schedule,因此他是沒法進行主動調度的,他只能因爲某些緣由致使陷入內核態時纔會被調度——好比中斷。線程
二、進程調度schedule()函數的跟蹤3d
如上圖,schedule——>__schedule——>pick_next_task().,pick_next_task這個函數封裝了進程調度算法,他返回下一個須要調度的進程。rest
,
code
當找到須要調度的函數後,經過調用 context_switch——>switch_to進行切換。下面咱們看下switch_to這個彙編宏blog
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 */ \ ——準備eip
__switch_canary \
"jmp __switch_to\n" /* regparm call */ \ ——新進程的eip 寫入eip寄存器,下面就正式切換到新進程了 ,這個爲啥jmp __switch_to,不用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)
三、jmp __switch_to
jmp是不會講下一條指令push到堆棧中的,這樣的話他返回時就返回的是nexp_ip。 若是用call,就會將下一條指令1: 壓棧,這樣返回時就必定會返回到這裏。
若是新的進程是剛建立的,他的next_ip不是1:, 而是 ret_from_fork,那麼若是用call不就有問題了