2021-2022-1 20212818《Linux内核原理与分析》第七周作业

一、实验内容

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

1、阅读理解task_struct数据结构(linux/sched.h at v3.18-rc6 · torvalds/linux · GitHub);

2、分析 fork 函数对应的内核处理过程 sys_clone,理解创建一个新进程如何创建和修改 task_struct 数据结构;

3、使用 gdb 跟踪分析一个 fork 系统调用内核处理函数 sys_clone ,验证您对 Linux 系统创建一个新进程的理解,推荐在实验楼 Linux 虚拟机环境下完成实验。 特别关注新进程是从哪里开始执行的?为什么从那里能顺利执行下去?即执行起点与内核堆栈如何保证一致;

二、实验过程

1、task_struct数据结构

        在Linux内核中,通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息。它定义在linux-3.18.6/include/linux/sched.h文件中。struct task_struct的数据结构非常庞大,struct task_struct的state是进程状态,stack是堆栈等,大概有400多行代码,如下代码摘录了struct task_struct数据结构的部分关键定义,并在每句后由相应的注释解释:

struct task_struct { 
 volatile long state;        //进程状态/* -1 unrunnable, 0 runnable, >0 stopped */
 void *stack;                // 指定进程内核堆栈
 pid_t pid;                  //进程标识符
 unsigned int rt_priority;   //实时优先级
 unsigned int policy;        //调度策略
 struct files_struct *files; //系统打开文件
 ...
}

        可以通过下图 所示的进程描述符的结构示意图从总体上看清struct task_struct的结构关系,比如进程的状态、进程双向链表的管理,以及控制台tty、文件系统fs的描述、进程打开文件描述符files、内存管理的描述mm,进程间的通信信号signal的描述等。

2021-2022-1 20212818《Linux内核原理与分析》第七周作业

 2、分析内核处理过程 sys_clone,理解task_struct 数据结构创建和修改

         系统调用服务例程sys_clone, sys_fork, sys_vfork三者最终都是调用do_fork函数完成。这三者的区别详细见下方链接:Linux中fork,vfork和clone详解(区别与联系)2021-2022-1 20212818《Linux内核原理与分析》第七周作业http://blog.csdn.net/gatieme/article/details/51417488        do_fork函数原型位于linux-3.18.6/kernel/fork.c。
        fork函数可以创建进程,创建一个进程是复制当前进程的信息,被复制的进程成为父进程,被创建的新进程成为子进程。父进程和子进程的绝大部分信息是完全一样的,但是有些信息不能一样,比如pid的值和内核堆栈。还有将新进程链接到各种链表中,要保存进程执行到哪个位置,有一个thread数据结构记录ip和sp等信息也不能一样。所以,父进程创建子进程时,会有一个地方复制父进程的进程描述符task_struct结构体变量,并有很多地方来修改复制的进程描述符task_struct结构体变量。

3、使用 gdb 跟踪分析一个 fork 系统调用内核处理函数 sys_clone

a、在MenuOS中添加fork函数,函数如下:

#include <unistd.h> 
int Fork(int argc, char *argv[]) 
{
int pid;
/* fork another process */
pid = fork();                                 
if (pid<0) 
{ 
/* error occurred */
fprintf(stderr,"Fork Failed!");
exit(-1);
} 
else if (pid==0)                              
{
/*   child process  */
printf("This is Child Process!\n");
} 
else 
{   
/*  parent process   */
printf("This is Parent Process!\n");          
/* parent will wait for the child to complete*/
wait(NULL);
printf("Child Complete!\n");
}
}

b、在main函数中加入如下代码

MenuConfig("fork","Fork a new process",Fork);

c、在menu目录下执行make rootfs命令,结果如下图所示(里面有fork命令):

2021-2022-1 20212818《Linux内核原理与分析》第七周作业

d、进行gdb跟踪调试,在sys_clone、do_fork、dup_task_struct、copy_process、copy_thread、ret_from_fork等处各设置断点,执行过程如下图所示:

2021-2022-1 20212818《Linux内核原理与分析》第七周作业

e、 开始执行,发现只输出了一个命令描述,后面并没有执行,即停在了sys_clone,如下图所示:

2021-2022-1 20212818《Linux内核原理与分析》第七周作业

f、再继续执行,停在copy_process,如下图所示:

2021-2022-1 20212818《Linux内核原理与分析》第七周作业

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

2021-2022-1 20212818《Linux内核原理与分析》第七周作业

 三、实验总结

        1、操作系统内核三大功能是进程管理,内存管理,文件系统,最核心的是进程管理。

        2、fork系统调用会创建一个当前进程的子进程。C语言库函数中的fork()在父进程中的返回值为子进程的pid,在子进程中的返回值为0。我们可以根据返回值的不同令父进程和子进程分别执行各自的任务。

        3、fork系统调用与其它系统调用相似,都要利用int 0x80指令产生中断,然后由操作系统进行关闭中断和保护现场的工作,通过查询系统调用表找到fork系统调用的入口地址。这个入口一直一般为sys_clone, sys_fork, sys_vfork中的一个,这三个入口最终都会调用do_fork()函数。C库函数中的fork()函数会调用sys_clone。

上一篇:进程(一)------进程管理


下一篇:Linux的.a、.so和.o文件 windows下obj,lib,dll,exe的关系 动态库内存管理 动态链接库搜索顺序 符号解析和绑定 strlen函数的汇编实现分析