Linux内核分析第六周作业

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

首先更新MenuOS的代码,加入调用fork的命令。吐槽一句,实验楼免费用户无法连网。还好只要去github复制一段代码即可

先观察一下fork命令的实现

 1 int Fork(int argc, char *argv[])
 2 {
 3     int pid;
 4     /* fork another process */
 5     pid = fork();
 6     if (pid<0)
 7     {
 8         /* error occurred */
 9         fprintf(stderr,"Fork Failed!");
10         exit(-1);
11     }
12     else if (pid==0)
13     {
14         /*     child process     */
15         printf("This is Child Process!\n");
16     }
17     else
18     {
19         /*     parent process     */
20         printf("This is Parent Process!\n");
21         /* parent will wait for the child to complete*/
22         wait(NULL);
23         printf("Child Complete!\n");
24     }
25 }

根据fork系统调用的返回值,可以区分出当前是父进程还是子进程,或者调用失败。然后父进程通过wait系统调用等待子进程结束,再输出"Child Complete"

因此,为了能退出进程,为quit命令添加一行代码

1 int Quit(int argc, char *argv[])
2 {
3     exit(0);
4 }

编译以后运行一下

可以看到结果是符合预期的。

吃个饭回来发现实验楼的虚拟机到时间了 (╯°Д°)╯︵ ┻━┻。。。。启动vmware继续

下面开始具体分析进程创建的过程。

Linux有三个系统调用可以创建进程:clone/vfork/fork,网上有许多这三个函数区别的总结,基本上就是父进程和子进程共享资源的区别。

代码上看,三个系统调用都是通过do_fork实现的,只不过参数不同。而do_fork又主要是由copy_process实现的

于是用gdb直接在copy_process入口处设置断点,进行跟踪。

一上来先是检查参数

然后调用dup_task_struct,创建了当前进程task_struct和thread_info结构体的副本。

但是创建的只是结构体的浅复制,结构体内的指针指向的都是同样的地址。于是接下来的一系列copy函数对各个模块进行深度复制

这些copy函数根据do_fork的参数进行复制,如果不复制,就等于共享了父进程的资源。并且复制也不是单纯的直接复制,而是根据情况进行一定的修改。

copy_thread为例

子进程复制了父进程的用户态寄存器,并且把eax设置为0。这样子进程从系统调用返回的时候,返回值就是0。所以用户态程序根据这一点来区别是哪个进程。

这一行把子进程的起始地址设置为ret_from_fork。当前进程作为父进程,还有后续很多工作要做,而当各种工作完成以后,子进程一开始就可以从ret_from_fork处运行

ret_from_fork代码很简单,基本就是直接从系统调用返回用户态,因此并不需要很多堆栈信息,只要保存基本的用户态寄存器等信息用于从系统调用顺利返回即可。

除了起始IP和堆栈,还有其他一些要修改的信息,包括使用alloc_pid分配一个新的PID,设置parent指针,明确父子关系等等

最后父进程返回do_fork,调用wake_up_new_task设置子进程的运行状态,子进程就可以被内核调度起来。父进程按照系统调用的正常返回流程返回,并不走ret_from_fork。

通过在ret_from_fork设置断点也可以证明这一点,

断点只触发了1次。

总结

Linux的进程创建方式是通过对父进程的复制实现的。通过这种方式,进程与线程的模型可以统一。而且引入了cow以后,创建新进程的开销极小,是Linux的优势之一。

王岩

原创作品转载请注明出处

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

时间: 2024-08-02 06:55:48

Linux内核分析第六周作业的相关文章

LINUX内核分析第六周学习总结——进程的描述和进程的创建

LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 进程的描述 进程描述符task_struct数据结构(一) 进程描述符task_struct数据结构(二) 进程的创建 进程的创建概览及fork一个进程的用户态代码 理解进程创建过程复杂代码的方法 浏览进程创建过程相关的关键代码 创建的新进程是从哪里开始执行的

魏昊卿——《Linux内核分析》第二周作业:了解操作系统是怎样工作的

魏昊卿——<Linux内核分析>第二周作业:了解操作系统是怎样工作的 一.实验部分 使用实验楼的虚拟机打开shell cd LinuxKernel/linux-3.9.4 qemu -kernel arch/x86/boot/bzImage 然后cd mykernel 您可以看到qemu窗口输出的内容的代码mymain.c和myinterrupt.c 使用自己的Linux系统环境搭建过程参见mykernel,其中也可以找到一个简单的时间片轮转多道程序内核代码 mymain.c myinterr

Linux内核分析第五周作业

分析system_call中断处理过程 这次的目标是通过gdb来跟踪上周选择的uname系统调用.因为系统调用是通过中断在内核态实现的,gdb无法调试本机的系统调用.所以必须像之前的内核跟踪那样,用gdb远程连接至qemu虚拟机进行跟踪. 1. 首先修改之前的MenuOS,添加一个myuname函数通过API的方式调用uname系统调用,直接复制了上周的代码,注意要在main函数中把这个函数添加到菜单中 用make命令编译以后,会生成一个test的可执行文件,可以先在本地运行试一试 可以看到命令

Linux内核分析第七周作业

Linux内核如何装载和启动一个可执行程序 有了上次的教训,这次直接用vmware完成 (- ̄3 ̄)- 先观察MenuOS新增的函数 1 int Exec(int argc, char *argv[]) 2 { 3 int pid; 4 /* fork another process */ 5 pid = fork(); 6 if (pid < 0) 7 { 8 /* error occurred */ 9 fprintf(stderr,"Fork Failed!"); 10 e

linux内核分析 第六周读书笔记

第三章 进程管理 3.1 进程 进程:处于执行期的程序 线程是在进程活动中的对象:内核调度的对象是线程而不是进程,在Linux系统中,并不区分线程和进程 在现代操作系统中, 进程提供两种虚拟机制:虚拟内存器和虚拟内存. 进程在创建它的时刻开始存活,这通常是调用fork系统的结果.该系统调用通过复制一个现有进程来创建一个全新的进程.fork系统调用从内核返回两次,一次到父进程,另一次回到新产生的子进程. 通常,创建新的进程都是为了立即执行新的不同的程序,而接着调用exec()这组函数就可以创建新的

linux内核分析第六周-分析Linux内核创建一个新进程的过程

Linux内核对进程管理是操作系统的重要任务之一. 此次实验就是了解内核创建一个新进程的大致过程. 为了简单,使用fork再用户态创建一个进程.代码如下: 下面是准备工作??? cd LinuxKernel rm -rf menu git clone https://github.com/mengning/menu.git cd menu mv test_fork.c test.c make rootfs 打开gdb进行远程调试? 设置断点 b sys_clone b do_fork b dup

linux内核分析 第六周 分析Linux内核创建一个新进程的过程

进程的描述 操作系统的三大管理功能:进程管理.内存管理.文件系统 为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. 进程控制块PCB task_struct:进程状态.进程打开的文件.进程优先级信息 task_struct总体数据结构的抽象: tty:控制台 fs:文件系统 files:文件描述符 mm:内存管理 signal:信号描述 进程的状态: 注意:Linux下,中就绪状态和运行状态都是TASK_RUNNING 一.gdb跟踪分析一个fork系统调

Linux内核分析——第六周学习笔记20135308

第五周 进程的描述和进程的创建 一.进程描述符task_struct数据结构 1.操作系统三大功能 进程管理 内存管理 文件系统 2.进程控制块PCB——task_struct 也叫进程描述符,为了管理进程,内核需要对每个进程进行描述,它就提供了内核所需了解的进程信息. struct task_struct数据结构很庞大,1235行~1644行 3.Linux进程状态 Linux进程的状态与操作系统原理中的描述的进程状态有所不同 操作系统状态: 就绪态 运行态 阻塞态 linux进程状态: 4.

20135327郭皓--Linux内核分析第六周 进程的描述和进程的创建

进程的描述和进程的创建 一.进程的描述 操作系统三大功能: 进程管理 内存管理 文件系统 进程描述符task_struct数据结构 task _ struct:为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. 进程的状态:Linux进程的状态(就绪态.运行态.阻塞态) 进程的标示pid:用来标示进程 进程描述符task_struct数据结构(重要部分): 1 struct task_struct { 2 volatile long state; /* 进程