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