LinuxLab3---结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程
一、实验要求
结合中断上下文切换和进程上下文切换分析Linux内核一般执行过程
- 以fork和execve系统调用为例分析中断上下文的切换
- 分析execve系统调用中断上下文的特殊之处
- 分析fork子进程启动执行时进程上下文的特殊之处
- 以系统调用作为特殊的中断,结合中断上下文切换和进程上下文切换分析Linux系统的一般执行过程
完成一篇博客总结分析Linux系统的一般执行过程,以期对Linux系统的整体运作形成一套逻辑自洽的模型,并能将所学的各种OS和Linux内核知识/原理融通进模型中。
二、fork函数
1.fork函数功能
fork函数系统调用创建一个子进程,子进程复制了父 进程中所有的进程信息,包括内核堆栈、进程描述符等,子进程作为一个独立的进程也会被调度,当子进程获得CPU开始运行时,从用户态空间来看,就是fork系统调用的下一条指令。但fork系统调用在子进程当中也是返回的,也就是说fork系统调用在内核里面变成了父子两个进程,父进程正常fork系统调用返回到用户态,fork出来的子进程也要从内核里返回到用户态。
2.fork函数过程
主要调用过程
- _do_fork
- copy_process
复制进程描述符和执⾏时所需的其他数据结构- dup_task_struct
复制进程描述符task_struct、创建内核堆栈等 - copy_thread_tls
初始化⼦进程内核栈和thread
- dup_task_struct
- wake_up_new_task
将⼦进程添加到就绪队列
- copy_process
- 系统调用返回
系统调用号57


调用_do_fork函数

_do_fork函数主要调用copy_process和wake_up_new_task函数

copy_process函数
复制进程描述符

复制所有进程信息,初始化子进程内核栈

子进程从ret_from_fork开始执行

总体过程

fork调用会有两次返回。第⼀次返回到原来的⽗进程的位置继续向下执⾏,这和其他的系统调⽤是⼀样的。
在⼦进程中fork也返回了⼀次,会返回到⼀个特定的点ret_from_fork,通过内核构造的堆栈环境,它可以正常系统调⽤返回到⽤户态。
三、execve函数
整体的调用关系为如下
- __x64_sys_execve
- do_execve()
- do_execveat_common()
- __do_execve_file
- exec_binprm()
- search_binary_handler()
- load_elf_binary()
- start_thread()
代码分析
调用do_execve

对参数进行了类型转换,调用do_execveat_common

调用__do_execve_file

打开要加载的可执行文件

交给真正的可执行文件加载器

查找可执行文件处理函数
寻找能够解析当前可执行文件的代码并加载


load_elf_binary中进行了一系列操作,按照ELF文件格式加载,然后启动新的进程

将之前pt_regs保存的堆栈和返回地址设置为新的ip,返回用户态时转而执行elf_entry指向的代码 
四、Linux系统的一般执行过程
Linux中最基本和一般的场景,正在运行的的用户进程X切换到用户进程Y的过程。
(1)正在运⾏的⽤户态进程X。
(2)发⽣中断(包括异常、系统调⽤等),硬件完成
当前CPU上下文压入用户态进程X的内核堆栈。
加载当前进程内核堆栈相关信息,跳转到中断处理程序,即中断执行路径的起点。
(3)保存现场,完成中断上下文切换,从进程X的用户态到进程X的内核态
(4)中断处理过程中或中断返回前调⽤了schedule函数,其中完成了进程调度算法选择next进程、进程地址空间切换、关键的进程上下⽂切换等。
(5)switch_to调⽤了__switch_to_asm汇编代码做了关键的进程上下⽂切换。将当前进程X的内核堆栈切换到进程调度算法选出来的next进程(本例假定为进程Y)的内核堆栈,并完成了进程上下⽂所需的指令指针寄存器状态切换。之后开始运⾏进程Y。
(6)中断上下⽂恢复,与(3)中断上下⽂切换相对应。注意这⾥是进程Y的中断处理过程中,⽽(3)中断上下⽂切换是在进程X的中断处理过程中,因为内核堆栈从进程X切换到进程Y了。
(7)iret pop cs:rip/ss:rsp/rflags,从Y进程的内核堆栈中弹出(3)中对应的压栈内容。此时完成了中断上下⽂的切换,即从进程Y的内核态返回到进程Y的⽤户态。
(8)继续运⾏⽤户态进程Y。

浙公网安备 33010602011771号