2018-2019-1 20189213《Linux内核原理与分析》第七周作业

进程的描述和进程的创建

1.课本基础知识汇总

操作系统内核实现操作系统的三大管理功能:即进程管理、内存管理、文件系统。
对进程的描述:
在操作系统原理中,通过进程控制块PCB描述进程;
在Linux内核中,通过一个数据结构struct task_struct来描述进程。

对进程状态的描述:
在操作系统原理中,进程有就绪态、运行态和阻塞态;
在Linux内核中,就绪态和运行态都是相同的TASK_RUNNING状态,另外再加上一个阻塞态;当进程是TASK_RUNNING状态时,它是可运行的,也就是就绪态,是否在运行取决于它有没有获得CPU的控制权,也就是说这个进程有没有在CPU中实际执行。如果在CPU中实际执行了,进程状态就是运行态;如果被内核调度出去了,在等待队列里就是就绪态。
对于一个正在运行的进程,调用用户态库函数exit()会陷入内核执行该内核函数do_exit(),进程会进入TASK_ZOMBIE状态,即进程的终止状态。TASK_ZOMBIE状态的进程一般叫做僵尸进程,Linux内核会在适当的时候把僵尸进程给处理掉,之后释放进程描述符。一个正在运行的进程在等待特定事件或资源时会进入阻塞态,阻塞态也分为两种:TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE。前者可以被信号和wake_up()唤醒,而后者只能被wake_up()唤醒。

进程的创建

Linux中创建进程一共有三个函数:

  1. fork,创建子进程;
  2. vfork,与fork类似,但是父子进程共享地址空间,而且子进程先于父进程运行;
  3. clone,主要用于创建线程。

进程创建的相关主要代码:

通过上面的代码可以看出,fork、vfork和clone这3个系统调用和kernel_thread内核函数都可以创建一个新进程,而且都是通过do_fork函数来创建进程的,只不过传递的参数不同。

do_fork函数的参数:
clone_flags:子进程创建相关标志,通过此标志可以对父进程的资源进行有选择的复制。
stack_start:子进程用户态堆栈的地址。
regs:指向pt_regs结构体的指针。当发生系统调用时,int指令和SAVE_ALL保存现场等会将CPU寄存器中的值按顺序压入内核栈。为了便于访问操作,这部分数据被定义为pt_regs结构体。
stack_size:用户态栈的大小,通常不必要,设置为0。
parent_tidptr和child_tidptr:父进程、子进程用户态下的pid地址。

进程创建中的几个关键函数:
do_fork():创建进程。
copy_process():创建进程内容(调用dup_task_struct、信息检查、初始化、更改进程状态、复制其他进程资源、调用copy_thread初始化子进程内核栈、设置子进程pid等)。
dup_task_struct():复制当前进程(父进程)描述符task_struct,分配子进程内核栈。
copy_thread():内核栈关键信息初始化。

实验:分析Linux内核创建一个新进程的过程

本实验主要用来跟踪分析进程创建的完整过程,首先我们在MenuOS里增加一个命令fork:

然后MenuOS正常运行,发现里面也多了fork命令,我们执行fork命令:

下面我们使用gdb来进行跟踪调试:
由于fork指令实际上执行的就是sys_clone,所以我们可以在sys_clone、do_fork、dup_task_struct、copy_process、copy_thread、ret_from_fork等处各设置断点:

然后开始执行,发现只输出了一个命令描述,后面并没有执行,而是停在了sys_clone这里:

再继续执行,停在copy_process:

再继续执行,停在dup_task_struct函数,进入dup_task_struct内部,将当前进程内核压栈压得那一部分寄存器复制到子进程中,以及赋值子进程的起点。

问题

为什么在创建子进程的时候,直接拷贝父进程的堆栈信息到子进程中?
答:为了返回时是一致的状态。
子进程退出时是否发生进程切换呢?
答:在syscall_exit中会调用syscall_exit_work函数。然后调用resume_userspace的时候就有可能会发生进程切换。
使用fork系统调用创建进程时,是否是先打印父进程后打印子进程?
答:父子进程的执行顺序和相应的调度算法密切相关,执行时执行顺序是不确定的。

总结

进程是通过进程控制块PCB来进行描述的,而内核要描述进程的结构,我们也称其为进程描述符,下图是进程描述符的结构示意图:

实际上,用户空间的寄存器、用户态堆栈等信息在切换到内核态的上下文时保存在内核栈中,父进程在内核态(dup_task_struct)复制出子进程,但子进程作为一个独立的进程,之后被调度运行时必须有一个指令地址,进程切换时,ip地址及当前内核栈的位置esp都存在于thread_info中,由copy_thread设置其thread.ip指向ret_from_fork作为子进程执行的第一条语句,并完成了内核态到用户态的切换。

posted @ 2018-11-25 20:10  20189213桂方平  阅读(157)  评论(0编辑  收藏  举报