linux进程上下文切换的具体过程,Linux实验三 结合中断上下文切换和进程上下文切换分析Linux内核一般执行过程...
fork系統(tǒng)調(diào)?創(chuàng)建?進(jìn)程,也就?個(gè)進(jìn)程變成了兩個(gè)進(jìn)程,兩個(gè)進(jìn)程執(zhí)?相同的代碼,只是fork系統(tǒng)調(diào)?在?進(jìn)程和?進(jìn)程中的返回值不同。
打開(kāi)linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl 文件,56、 57、 58號(hào)系統(tǒng)調(diào)?__x64_sys_clone、 __x64_sys_fork、__x64_sys_vfork,即如下kernel/fork.c代碼。
進(jìn)入linux-5.4.34/kernel/fork.c 查看 fork 源代碼:
/** Create a kernel thread.*/pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned longflags)
{return _do_fork(&args);
}
SYSCALL_DEFINE0(fork)
{return _do_fork(&args);
}
SYSCALL_DEFINE0(vfork)
{return _do_fork(&args);
}
SYSCALL_DEFINE5(clone, unsignedlong, clone_flags, unsigned long, newsp,
{return _do_fork(&args)
}
通過(guò)上?的代碼可以看出fork、vfork和clone這3個(gè)系統(tǒng)調(diào)?,以及do_fork和kernel_thread內(nèi)核函數(shù)都可以創(chuàng)建?個(gè)新進(jìn)程,?且都是通過(guò)_do_fork函數(shù)來(lái)創(chuàng)建進(jìn)程的,只不過(guò)傳遞的參數(shù)不同。
_do_fork函數(shù)主要完成了調(diào)?copy_process()復(fù)制?進(jìn)程、獲得?wake_up_new_task將?進(jìn)程加?就緒隊(duì)列等待調(diào)度執(zhí)?等。
//_do_fork關(guān)鍵部分代碼
long _do_fork(struct kernel_clone_args *args)
{//復(fù)制進(jìn)程描述符和執(zhí)?時(shí)所需的其他數(shù)據(jù)結(jié)構(gòu)
p =copy_process(NULL, trace, NUMA_NO_NODE, args);
wake_up_new_task(p);//將?進(jìn)程添加到就緒隊(duì)列
return nr;//返回?進(jìn)程pid(?進(jìn)程中fork返回值為?進(jìn)程的pid)
}
copy_process()是創(chuàng)建?個(gè)進(jìn)程的主要的代碼。如下是copy_process()函數(shù)的關(guān)鍵代碼,完整代碼?kernel/fork.c
static __latent_entropy struct task_struct *copy_process(struct pid *pid,inttrace,intnode,struct kernel_clone_args *args)
{//復(fù)制進(jìn)程描述符task_struct、創(chuàng)建內(nèi)核堆棧等
p =dup_task_struct(current, node);/*copy all the process information*/shm_init_task(p);
…//初始化?進(jìn)程內(nèi)核棧和thread
retval = copy_thread_tls(clone_flags, args->stack, args->stack_size, p,
args->tls);
…return p;//返回被創(chuàng)建的?進(jìn)程描述符指針
}
copy_process函數(shù)主要完成了:
調(diào)?dup_task_struct復(fù)制當(dāng)前進(jìn)程(?進(jìn)程)描述符task_struct
信息檢查、初始化、把進(jìn)程狀態(tài)設(shè)置為T(mén)ASK_RUNNING(此時(shí)?進(jìn)程置為就緒態(tài))、采?寫(xiě)時(shí)復(fù)制技術(shù)逐?復(fù)制所有其他進(jìn)程資源
調(diào)?copy_thread_tls初始化?進(jìn)程內(nèi)核棧
設(shè)置?進(jìn)程pid等。
接下來(lái)具體看dup_task_struct和copy_thread_tls
dup_task_struct作用:
在專(zhuān)業(yè)高速緩沖內(nèi)存上分配task_struct,并完成初始化
在普通內(nèi)存中分配thread_info及連續(xù)的兩個(gè)頁(yè)面,完成初始化
將task_struct和thread_info聯(lián)系起來(lái)
主要代碼:
static struct task_struct *dup_task_struct(struct task_struct *orig, intnode)
{…//實(shí)際完成進(jìn)程描述符的拷?,具體做法是*tsk = *orig
err =arch_dup_task_struct(tsk, orig);
…
tsk->stack =stack;
...//實(shí)際完成進(jìn)程描述符的拷?,具體做法是*tsk = *orig
setup_thread_stack(tsk, orig);
clear_user_return_notifier(tsk);
clear_tsk_need_resched(tsk);
set_task_stack_end_magic(tsk);…
}
copy_thread_tls作用:
負(fù)責(zé)構(gòu)造fork系統(tǒng)調(diào)?在?進(jìn)程的內(nèi)核堆棧,也就是fork系統(tǒng)調(diào)?在??進(jìn)程各返回?次,?進(jìn)程中和其他系統(tǒng)調(diào)?的處理過(guò)程并??致,?在?進(jìn)程中的內(nèi)核函數(shù)調(diào)?堆棧需要特殊構(gòu)建,為?進(jìn)程的運(yùn)?準(zhǔn)備好上下?環(huán)境。
主要代碼:
int copy_thread_tls(unsigned long clone_flags, unsigned longsp,
unsignedlong arg, struct task_struct *p, unsigned longtls)
{
frame->ret_addr = (unsigned long) ret_from_fork;
p->thread.sp = (unsigned long) fork_frame;*childregs = *current_pt_regs();
childregs->ax = 0;
.../** Set a new TLS for the child thread?*/
if (clone_flags &CLONE_SETTLS) {
err= do_arch_prctl_64(p, ARCH_SET_FS, tls);
do_fork?總結(jié)
進(jìn)程的創(chuàng)建過(guò)程?致是?進(jìn)程通過(guò)fork系統(tǒng)調(diào)?進(jìn)?內(nèi)核_do_fork函數(shù),如下圖所示復(fù)制進(jìn)程描述符及相關(guān)進(jìn)程資源(采?寫(xiě)時(shí)復(fù)制技術(shù))、分配?進(jìn)程的內(nèi)核堆棧并對(duì)內(nèi)核堆棧和thread等進(jìn)程關(guān)鍵上下?進(jìn)?初始化,最后將?進(jìn)程放?就緒隊(duì)列, fork系統(tǒng)調(diào)?返回;??進(jìn)程則在被調(diào)度執(zhí)?時(shí)根據(jù)設(shè)置的內(nèi)核堆棧和thread等進(jìn)程關(guān)鍵上下?開(kāi)始執(zhí)?。
2?execve系統(tǒng)調(diào)用
execve(執(zhí)行文件)在父進(jìn)程中fork一個(gè)子進(jìn)程,在子進(jìn)程中調(diào)用exec函數(shù)啟動(dòng)新的程序。exec函數(shù)一共有六個(gè),其中execve為內(nèi)核級(jí)系統(tǒng)調(diào)用,其他(execl,execle,execlp,execv,execvp)都是調(diào)用execve的庫(kù)函數(shù)。
表頭文件:
#include
定義函數(shù):
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
execve()用來(lái)執(zhí)行參數(shù)filename字符串所代表的文件路徑,第二個(gè)參數(shù)是利用指針數(shù)組來(lái)傳遞給執(zhí)行文件,并且需要以空指針(NULL)結(jié)束,最后一個(gè)參數(shù)則為傳遞給執(zhí)行文件的新環(huán)境變量數(shù)組。成功無(wú)返回值,失敗返回-1。
execve系統(tǒng)調(diào)過(guò)程:
sys_execve-->do_execve-->do_execveat_common-->__do_execve_file-->search_binary_handler-->load_elf_binary-->start_thread
execve陷入內(nèi)核,傳入命令行參數(shù)和shell上下文環(huán)境
sys_execve調(diào)用do_execve封裝命令行參數(shù)和shell上下文
調(diào)用do_execveat_common,do_execveat_common調(diào)用__do_execve_file,打開(kāi)ELF文件并把信息的裝入linux_binprm結(jié)構(gòu)體
__do_execve_file中調(diào)用search_binary_handler,尋找解析ELF文件的函數(shù)
search_binary_handler找到ELF文件解析函數(shù)load_elf_binary,解析ELF文件,把ELF文件裝入內(nèi)存,修改進(jìn)程的用戶(hù)態(tài)堆棧,修改進(jìn)程的數(shù)據(jù)段代碼段
load_elf_binary調(diào)用start_thread修改進(jìn)程內(nèi)核堆棧
返回用戶(hù)態(tài),此時(shí)ip指向ELF文件的main函數(shù)地址
三?Linux的一般執(zhí)行過(guò)程
1) 正在運(yùn)?的?戶(hù)態(tài)進(jìn)程X。
2) 發(fā)?中斷(包括異常、系統(tǒng)調(diào)?等), CPU完成load cs:rip(entry of a specific ISR),即跳轉(zhuǎn)到中斷處理程序??。
3) 中斷上下?切換,具體包括如下?點(diǎn):
swapgs指令保存現(xiàn)場(chǎng),可以理解CPU通過(guò)swapgs指令給當(dāng)前CPU寄存器狀態(tài)做了?個(gè)快照。
rsp point to kernel stack,加載當(dāng)前進(jìn)程內(nèi)核堆棧棧頂?shù)刂返絉SP寄存器。快速系統(tǒng)調(diào)?是由系統(tǒng)調(diào)???處的匯編代碼實(shí)現(xiàn)?戶(hù)堆棧和內(nèi)核堆棧的切換。
save cs:rip/ss:rsp/rflags:將當(dāng)前CPU關(guān)鍵上下?壓?進(jìn)程X的內(nèi)核堆棧,快速系統(tǒng)調(diào)?是由系統(tǒng)調(diào)???處的匯編代碼實(shí)現(xiàn)的。
此時(shí)完成了中斷上下?切換,即從進(jìn)程X的?戶(hù)態(tài)到進(jìn)程X的內(nèi)核態(tài)。
4) 中斷處理過(guò)程中或中斷返回前調(diào)?了schedule函數(shù),其中完成了進(jìn)程調(diào)度算法選擇next進(jìn)程、進(jìn)程地址空間切換、以及switch_to關(guān)鍵的進(jìn)程上下?切換等。
5) switch_to調(diào)?了__switch_to_asm匯編代碼做了關(guān)鍵的進(jìn)程上下?切換。將當(dāng)前進(jìn)程X的內(nèi)核堆棧切換到進(jìn)程調(diào)度算法選出來(lái)的next進(jìn)程(本例假定為進(jìn)程Y)的內(nèi)核堆棧,并完成了進(jìn)程上下?所需的指令指針寄存器狀態(tài)切換。之后開(kāi)始運(yùn)?進(jìn)程Y(這?進(jìn)程Y曾經(jīng)通過(guò)以上步驟被切換出去,因此可以從switch_to下??代碼繼續(xù)執(zhí)?)。
6) 中斷上下?恢復(fù),與(3)中斷上下?切換相對(duì)應(yīng)。注意這?是進(jìn)程Y的中斷處理過(guò)程中,?(3)中斷上下?切換是在進(jìn)程X的中斷處理過(guò)程中,因?yàn)閮?nèi)核堆棧從進(jìn)程X切換到進(jìn)程Y了。
7) 為了對(duì)應(yīng)起?中斷上下?恢復(fù)的最后?步單獨(dú)拿出來(lái)(6的最后?步即是7) iret - pop cs:rip/ss:rsp/rflags,從Y進(jìn)程的內(nèi)核堆棧中彈出(3)中對(duì)應(yīng)的壓棧內(nèi)容。此時(shí)完成了中斷上下?的切換,即從進(jìn)程Y的內(nèi)核態(tài)返回到進(jìn)程Y的?戶(hù)態(tài)。注意快速系統(tǒng)調(diào)?返回sysret與iret的處理略有不同。
8) 繼續(xù)運(yùn)??戶(hù)態(tài)進(jìn)程Y。
總結(jié)
以上是生活随笔為你收集整理的linux进程上下文切换的具体过程,Linux实验三 结合中断上下文切换和进程上下文切换分析Linux内核一般执行过程...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: html5怎么跟安卓交互,html5怎么
- 下一篇: Java的getcelltype用法_j