pointfish 2020-06-14
请参考上一篇博客:https://www.cnblogs.com/pghzl-123/p/12825669.html
参考文章:https://blog.csdn.net/zyn19950120/article/details/75948632
通过对start_kernel进行分析,我们会注意到Linux内核第一个进程的初始化;
其中,init_task为第?个进程(0号进程)的进程描述符结构体变量,它的初始化是通过硬编码?式固定下来的。除此之外,所有其他进程的初始化都是通过do_fork复制?进程的?式初始化的。
1号和2号进程的创建是start_kernel初始化到最后由rest_ init通kernel_thread创建了两个内核线程:?个是kernel_init,最终把?户态的进程init给启动起来,是所有?户进程的祖先;另?个是kthreadd内核线程,kthreadd内核线程是所有内核线程的祖先,负责管理所有内核线程。
kernel_thread创建进程的过程和shell命令?下启动?个进程时fork创建进程的过程在本质上是?样的,都要通过复制?进程来创建?个?进程。
_do_fork具体进程的创建?概就是把当前进程的描述符等相关进程资源复制?份,从?产??个?进程,并根据?进程的需要对复制的进程描述符做?些修改,然后把创建好的?进程放?运?队列(操作系统原理中的就绪队列)。在进程调度时,新创建的?进程处于就绪状态有机会被调度执?。
通过前文,我们大致了解了系统调用的大致处理过程。fork也是一个系统调用,和一般的系统执行过程大致是一样的。尤其从父进程的角度来看,fork的执行过程与一般的系统调用完全一致。
但问题是:fork系统调?创建了?个?进程,?进程复制了?进程中所有的进程信息,包括内核堆栈、进程描述符等,?进程作为?个独?的进程也会被调度,当?进程获得CPU开始运?时,它是从哪?开始运?的呢?从?户态空间来看,就是fork系统调?的下?条指令。但fork系统调?在?进程当中也是返回的,也就是说fork系统调?在内核??变成了??两个进程,?进程正常fork系统调?返回到?户态,fork出来的?进程也要从内核?返回到?户态。那么对于?进程来讲,fork系统调?在内核处理程序中是从何处开始执?的呢??个新创建的?进程是从哪?代码开始执?的,这是?个关键问题。
fork与一般系统调用的不同之处在于有两次返回:
正常的?个系统调?都是陷?内核态,再返回到?户态,然后继续执?系统调?后的下?条指令。fork和其他系统调?不同之处是它在陷?内核态之后有两次返回,第?次返回到原来的?进程的位置继续向下执?,这和其他的系统调?是?样的。在?进程中fork也返回了?次,会返回到?个特定的点——ret_from_fork,通过内核构造的堆栈环境,它可以正常系统调?返回到?户态
执行fork时从用户态到内核态的大致过程图如下所示:
1)通过对内核源码进行分析,fork、vfork、clone这三个系统调用和kernel_thread都可以创建一个新进程,而且都是通过do_for函数来创建的,只是传递的参数不一样。
接下来我们直接对do_fork进行分析,源码位于/linux/kernel/fork.c目录下。
fork的系统调用号
系统调用过程如下:
(1) 通过系统调用号宏以及_syscal()l函数结合cpu寄存器和0x80中断从用户空间到内核空间;
(2)进入内核空间之后,通过system_call对eax寄存器中的系统调用号以及其他寄存器传入的参数进行保存(SAVE ALL),并通过sys_call_table系统调用表进行查询,找到内部系统调用函数sys_fork(),调用完后,将返回值通过eax寄存器带回用户态,然后RESTORE_ALL,将各个寄存器的值pop.
(3)fork()函数在内核中的实现如图:
3、编写程序,使用fork函数:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main(int argc, char* argv[]) { int pid; pid = fork(); if(pid<0) { //error fprintf(stderr,"For Failed"); exit(-1); } else if(pid==0) { //child printf("this is child process \n"); } else { //parent printf("this is Parent process \n"); wait(NULL); printf("child complete \n"); } return 0; }
执行结果:
gdb调试跟踪:
开启虚拟机,在__x64_sys_clone
,_do_fork
,cpoy_process
,dup_task_struct
,copy_thread_tls
下断点,shell下运行fork
可执行文件,查看此时函数栈
do_fork主要完成了调用copy_process()复制了父进程的信息、获得pid、调用wake_up_new_task将子进程加入调度队列等待获得分配CPU资源运行,进程的创建?作就完成了,?进程就可以等待调度执?,?进程的执?从这?设定的ret_from_fork开始。
copy_process是创建一个进程的主要代码。dup_task_struct复制当前进程(?进程)描述符task_struct、信息检查、初始化、把进程状态设置为TASK_RUNNING(此时?进程置为就绪态)、采?写时复制技术逐?复制所有其他进程资源、调?copy_thread_tls初始化?进程内核栈、设置?进程pid等。其中最关键的就是dup_task_struct复制当前进程(?进程)描述符task_struct和copy_thread_tls初始化?进程内核栈。
如下图所示:
fork执行的整个过程图示如下 :
(copy_thread_tls在早期版本3.18.6该函数叫copy_thread)
execve()系统调用的实质是运行的内核态的sys_execve()函数,大致处理过程如下:
整体的调?关系为
sys_execve()或__x64_sys_execve
-> do_execve() //读取128字节的文件头部,以此判断可执行文件的类型
–>do_execveat_common()
-> __do_execve_file
-> exec_binprm()
-> search_binary_handler() //去搜索和匹配合适的可执行文件装载处理过程
->load_elf_binary() //ELF文件由load_elf_binary()负责装载
-> start_thread() //由load_elf_binary()调用负责创建新进程的堆栈
进程切换和系统的一般执行过程:
1、进程调度的时机
1)中断:中断在本质上都是软件或者硬件发?了某种情形?通知处理器的?为,处理器进?停?正在运?的当前进程,对这些通知做出相应反应,即转去执?预定义的中断处理程序(内核代码??),这就需要从进程的指令流?切换出来
中断能起到暂停当前进程指令流(Linux内核中称为thread)转去执?中断处理程序的作?,中断处理程序是与当前进程指令流独?的内核代码指令流。从?户程序的?度看进程调度的时机?般都是中断处理后和中断返回前的时机点进?,只有内核线程可以直接调?schedule函数主动发起进程调度和进程切换。
中断的类型:
2)schedule函数:Linux内核通过schedule函数实现进程调度,schedule函数负责在运?队列中选择?个进程,然后把它切换到CPU上执?。
调?schedule函数的时机主要分为两类:
2、上下文
一般来说,CP任何时刻都处于以下三种情况之一:
3、简单总结进程调度时机
4、Linux调度策略
Linux系统中常?的?种调度策略为
SCHED_NORMAL是?于普通进程的调度类,
SCHED_FIFO和SCHED_RR是?于实时进程的调度类,优先级?于SCHED_NORMAL
CFS即为完全公平调度算法,其基本原理是基于权重的动态优先级调度算法。每个进程使?CPU的顺序由进程已使?的CPU虚拟时间(vruntime)决定,已使?的虚拟时间越少,进程排序就越靠前,进程再次被调度执?的概率也就越?。每个进程每次占?CPU后能够执?的时间(ideal_runtime)由进程的权重决定,并且保证在某个时间周期(__sched_period)内运?队列?的所有进程都能够?少被调度执??次。
5、进程上下文切换
为了控制进程的执?,内核必须有能?挂起正在CPU上运?的进程,并恢复执?以前挂起的某个进程。这种?为被称为进程切换,任务切换或进程上下?切换。尽管每个进程可以拥有属于??的地址空间,但所有进程必须共享CPU及寄存器。因此在恢复?个进程执?之前,内核必须确保每个寄存器装?了挂起进程时的值。进程恢复执?前必须装?寄存器的?组数据,称为进程的CPU上下?。
进程上下文包含了进程执行需要的所有信息:
进程切换就是变更进程上下文,最核心的是几个关键寄存器的的保存与变换:
进程切换关键环节示意图:
进程上下?切换时需要保存要切换进程的相关信息(如thread.sp与thread.ip),这与中断上下?的切换是不同的。
6、Linux系统的一般执行过程
以正在运行的用户态进程X切换到用户态进程Y为例具体表述如下:
关键点包括: