2023-2024-1 20232803《Linux内核原理与分析》第七周作业

本文详细解析了Linux内核中fork函数的工作原理,重点讨论了sys_clone内核处理过程以及如何通过do_fork创建新进程,涉及task_struct数据结构的复制和修改。通过实验分析了创建子进程的步骤,包括状态设置、资源复制和内核栈初始化。
摘要由CSDN通过智能技术生成

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

1. 前置知识

1.1 阅读理解task_struct数据结构

通过阅读task_struct源码,我们能够知道,在 linux 中每一个进程都由 task_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; //系统打开文件
 ...
}

task_struct 就是 PCB,是对进程控制的唯一手段也是最有效的手段。当我们调用 fork() 时, 系统会为我们产生一个 task_struct 结构,然后从父进程那里继承一些数据, 并把新的进程插入到进程树中, 以待进行进程管理。

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

系统调用服务例程sys_clone, sys_fork, sys_vfork三者最终都是调用do_fork函数完成。do_fork函数原型位于linux-3.18.6/kernel/fork.c

fork函数可以创建进程,创建一个新进程的过程是:复制当前进程的信息,被复制的进程成为父进程,被创建的新进程成为子进程。父进程和子进程的绝大部分信息是完全一样的,但是有些信息需要不同,比如pid的值和内核堆栈。还有将新进程链接到各种链表中,要保存进程执行到哪个位置,有一个thread数据结构记录ip和sp等信息也不能一样。所以,父进程创建子进程时,会有一个地方复制父进程的进程描述符task_struct结构体变量,并有很多地方来修改复制的进程描述符task_struct结构体变量。

2. 环境准备

使用命令cd ./LinuxKernel/menu进入menu目录,修改test.c文件。

test.c文件添加fork函数,代码如下:

#include <unistd.h> 
int Fork(int argc, char *argv[]) 
{
	int pid;
	pid = fork();                                 
	if (pid<0) 
	{ 
		fprintf(stderr,"Fork Failed!");
		exit(-1);
	} 
	else if (pid==0)                              
	{
		printf("This is Child Process!\n");
	} 
	else 
	{   
		printf("This is Parent Process!\n");          
		wait(NULL);
		printf("Child Complete!\n");
	}
}

然后在main函数中添加fork函数的调用命令,代码如下:

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

接着使用命令 make rootfs重新编译。编译完成后,在QEMU中使用help命令查看我们新增的命令,并输入fork测试该新增的命令。

在这里插入图片描述

3. 实验过程

先使用以下命令运行QEMU。

cd LinuxKernel
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

再另打开一个窗口,通过以下命令使用gdb进行调试。

gdb
file LinuxKernel/linux-3.18.6/vmlinux   //加载内核
target remote:1234          //链接到menu os

接着在sys_clonedo_forkdup_task_structcopy_processcopy_threadret_from_fork处设置断点。

b sys_clone
b do_fork
b dup_task_struct
b copy_process
b copy_thread
b ret_from_fork

设置完断点,如下图所示。

在这里插入图片描述

然后通过c开始执行,此时会在do_fork断点处停下来,接着通过c继续跟踪。

在这里插入图片描述

在这里插入图片描述

4. 实验分析

(1)fork()

库函数fork是用户态创建一个子进程的系统调用,实际上fork系统调用把当前进程又复制了一个子进程,也就是一个进程变成了两个进程,两个进程执行相同的代码,只是fork系统调用在父进程和子进程中的返回值不同。但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

(2)do_fork()

do_fork()主要完成了:调用copy_process()复制父进程信息、获得pid、调用wake_up_new_task将子进程加入调度器队列等待获得分配CPU资源运行、通过clone_flags标志做一些辅助工作,其中copy_process()是创建一个进程内容的主要的代码

(3)copy_process()

copy_process()主要是完成了调用dup_task_struct复制当前进程(父进程)描述符task_struct作为子进程的进程描述符、信息检查、初始化、把进程状态设置为TASK_RUNNING(此时子进程置为就绪态)、采用写时复制技术逐一复制所有其他进程资源、调用copy_thread初始化子进程内核栈、设置子进程pid等。

(4)copy_thread()

copy_thread()主要用于获取子进程寄存器信息的存放位置,并对子进程的thread.sp赋值,将来子进程运行,这就是子进程的esp寄存器的值。

5. 实验小结

(1)操作系统内核三大功能是进程管理,内存管理,文件系统,最核心的是进程管理。
(2)fork系统调用会创建一个当前进程的子进程。C语言库函数中的fork()在父进程中的返回值为子进程的pid,在子进程中的返回值为0。我们可以根据返回值的不同令父进程和子进程分别执行各自的任务。
(3)fork系统调用与其它系统调用相似,都要利用int 0x80指令产生中断,然后由操作系统进行关闭中断和保护现场的工作,通过查询系统调用表找到fork系统调用的入口地址。这个入口一直一般为sys_clone, sys_fork, sys_vfork中的一个,这三个入口最终都会调用do_fork()函数。C库函数中的fork()函数会调用sys_clone

使用AI工具辅助学习

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值