分析 Linux 内核创建一个新进程的过程
一、实验过程
1、task_struct 数据结构
Linux系统中的每个进程都有一个名为task_struct
的数据结构,它相当于“进程控制块(PCB)”,用于表示和管理每个正在运行的进程(或任务)。task_struct
结构体被定义在<include/linux/sched.h>
中,是非常重要的数据结构。它包含了有关进程的各种属性和状态信息,以便内核能够有效地管理和调度它们,该结构体的定义如下所示。
struct task_struct {
volatile long state; // 进程状态
void *stack; // 进程堆栈指针
unsigned int flags; // 进程标志
struct list_head tasks; // 进程链表
pid_t pid; // 进程ID
pid_t tgid; // 进程组ID
struct mm_struct *mm; // 内存管理结构
struct files_struct *files; // 文件描述符表
// ... 其他字段和信息 ...
};
2、分析 fork 函数对应的内核处理过程 sys_clone
fork 函数在Linux内核中的实现通常是通过 sys_clone 系统调用来完成的,sys_clone 是创建新进程的底层系统调用,该过程分析如下:
1.首先是fork触发系统调用sys_clone,然后最终执行的是do_fork。
2.fork创建了两个进程,一个父进程,一个子进程,其中子进程是对父进程的拷贝,它从父进程处复制了整个进程的地址空间,只有进程号和一些计时器等等是自己独有的,由于要复制很多资源,所以fork创建进程是很慢的。
3.fork执行一次有两次返回值,在父进程里返回新建的子进程编号,在子进程里返回0.由于创建进程的三种方式最终都是调用do_fork。
4.创建子进程的一个重要的过程是复制父进程里面的资源,有看到下面的copy_process函数,所以基本判断是复制资源的函数.这段代码是创建子进程的精华部分,所有的创建进程上下文,为新进程设置新的pid,还有复制代码,都在这一部分了。
3、使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证对Linux系统创建一个新进程的理解
1)打开shell并启动menuOS
代码如下
cd LinuxKernel
rm -rf menu
git clone https://github.com/mengning/menu.git
cd menu
mv test_fork.c test.c
make rootfs
测试fork功能
2)在shell中启动内核,打开调试模式
3)设置断点
在shell2中使用gdb调试,在sys_clone、do_fork、dup_task_struct、copy_process、copy_thread、ret_from_fork
处设置断点,命令如下:
cd LinuxKernel
gdb
file linux-3.18.6/vmlinux
target remote:1234
b sys_clone
b do_fork
b dup_task_struct
b copy_process
b copy_thread
b ret_from_fork
在gdb中continue执行到断点do_fork
处:
copy_process
和copy_thread
断点
二、实验总结
- 对“Linux 系统创建一个新进程”的理解
在Linux系统中,创建新进程的过程是通过 fork
系统调用完成的。fork系统调用从内核返回两次:一次回到父进程,另一次回到新产生的子进程。它实际上是由clone()系统调用实现的。子进程和父进程最大的区别就是在于他们拥有不同的PID。
- 分析新进程的执行起点及对应的堆栈状态
创建的新进程的执行起点:ret_from_fork
在Linux中,新进程的堆栈状态如下:
*childregs = *current_pt_regs(); //复制内核堆栈
childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!
p->thread.sp = (unsigned long) childregs;//调度到子进程时的内核栈顶
p->thread.ip = (unsigned long) ret_from_fork;//调度到子进程时的第一条指令地址 ip指向的是ret_from_fork,所以是从这里开始执行的。