進程切換過程之一:上下文切換
注:下面給出的源碼均出自https://github.com/mit-pdos/xv6-public
在進程進行切換時,需要進行上下文切換,也就是寄存器的值的保存。
對於上下文的切換,有兩種情況:硬件自動(隱式)完成、操作系統顯式保存。
下面分析兩段xv6的源代碼,都是上下文切換部分,版本有所不同:
注:每個進程都有一個用來維護過程調用的棧,有的書把這個棧叫做內核棧;在編譯原理中,這個棧的術語叫活動記錄,在下文中,提到的棧都是說的這個保存活動記錄的棧。每個進程都有自己的活動記錄(當然,如果是多CPU的情況下,就會引入線程的概念,則每個線程有一個活動記錄)
較老的版本
# void swtch(struct context *old, struct context *new);
#
# Save current register context in old
# and then load register context from new.
.globl swtch
swtch:
# Save old registers
movl 4(%esp), %eax
popl 0(%eax) # %eip
movl %esp, 4(%eax)
movl %ebx, 8(%eax)
movl %ecx, 12(%eax)
movl %edx, 16(%eax)
movl %esi, 20(%eax)
movl %edi, 24(%eax)
movl %ebp, 28(%eax)
# Load new registers
movl 4(%esp), %eax # not 8(%esp) - popped return address above
movl 28(%eax), %ebp
movl 24(%eax), %edi
movl 20(%eax), %esi
movl 16(%eax), %edx
movl 12(%eax), %ecx
movl 8(%eax), %ebx
movl 4(%eax), %esp
pushl 0(%eax) # %eip
這個版本的上下文結構struct context的內容如下:
struct context {
int eip; //程序計數器
int esp; //棧指針
int ebp;
int ecx;
int edx;
int esi;
int edi;
int ebp;
};
分析一下上下文切換的源代碼:
源代碼的上半段是將“老”的進程的上下文保存,下半段將“新”的進程(要切換到的)的上下文恢復。
執行swtch的指令時,棧的狀態:
movl 4(%esp), %eax
將old(參數)的值放入寄存器%eax。
popl 0(%eax)
然後將棧頂的值保存到0(%eax)的位置。也就是將返回地址(%eip)的值保存到old指向的context空間中
之後,就是將其他的寄存器放入old指向的context空間中。
通過上面的源碼,可以得到context在內存中分配空間的方式:
下面的代碼就是從new指向的context空間中,將各個寄存器的值恢復,就不詳細說明了。
較新的版本:就像一開始提到的,硬件會自動保存一部分
硬件會保存一部分的寄存器,還有剩餘的需要進行手動保存。
# Context switch
#
# void swtch(struct context **old, struct context *new);
#
# Save the current registers on the stack, creating
# a struct context, and save its address in *old.
# Switch stacks to new and pop previously-saved registers.
.globl swtch
swtch:
movl 4(%esp), %eax
movl 8(%esp), %edx
# Save old callee-saved registers
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi
# Switch stacks
movl %esp, (%eax)
movl %edx, %esp
# Load new callee-saved registers
popl %edi
popl %esi
popl %ebx
popl %ebp
ret
開始的兩條代碼,將old與new放入%eax和%edx,方便後面通過指針的方式訪問內存。
接下來的四條壓棧指令,直接將要手動保存的寄存器壓入棧中。
執行到這裏,“老”進程和“新”進程對應的棧的示意圖如下:
與之前舊版本的有些許不同,新版本的直接將上下文保存在進程自己的棧中;舊版本的保存在了其他的地方(暫時就理解到這個程度,由於沒有分析過所有的源碼,這裏的分析可能比較片面,但這是我整個的分析過程,如有有大佬看出哪裏不會,請指正)