一、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; //系统打开文件
...
}
二、分析 fork 函数对应的内核处理过程 sys_clone
do_fork函数原型位于linux-3.18.6/kernel/fork.c。
fork函数可以创建进程,创建一个进程是复制当前进程的信息,被复制的进程成为父进程,被创建的新进程成为子进程。父进程和子进程的绝大部分信息是完全一样的,但是有些信息不能一样,比如pid的值和内核堆栈。还有将新进程链接到各种链表中,要保存进程执行到哪个位置,有一个thread数据结构记录ip和sp等信息也不能一样。所以,父进程创建子进程时,会有一个地方复制父进程的进程描述符task_struct结构体变量,并有很多地方来修改复制的进程描述符task_struct结构体变量。
三、使用 gdb 跟踪分析一个 fork 系统调用内核处理函数 sys_clone
1、在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");
}
}
2、在make rootfs后,help指令可见我们的fork指令
3、进入gdb进行调试,在sys_clone、do_fork、dup_task_struct、copy_process、copy_thread、ret_from_fork等处各设置断点
4、不断执行之后,有如下的结果
总结:
1、fork函数执行过程如下:
①当调用 Fork
函数时,它会调用 fork()
系统调用来创建一个新的子进程。
②如果 fork()
返回一个负数,那意味着创建子进程失败,通常是由于系统资源不足等原因。在这种情况下,程序会打印错误消息 "Fork Failed!" 并退出,返回状态码 -1。
③如果 fork()
返回0,那说明这是子进程。子进程会打印 "This is Child Process!
④如果 fork()
返回一个正数,那说明这是父进程。父进程会打印 "This is Parent Process!"。
⑤父进程会继续执行,并在 wait(NULL)
调用中等待子进程的终止。这是为了确保父进程在子进程完成之前不会继续执行。
⑥一旦子进程完成,父进程会继续执行,并打印 "Child Complete!"。
2、经过分析和资料查找,可以得到如下结果:
①sys_clone
是Linux内核的系统调用,用于创建一个新进程(或线程)。这个系统调用接受一个或多个标志和参数,允许创建一个新的执行上下文,可以在同一地址空间或不同地址空间中运行。sys_clone
实际上是对 do_fork
函数的封装。
②do_fork
是Linux内核中的一个关键函数,负责创建新进程或线程。它会复制父进程的状态、资源和执行环境,并为新进程分配新的任务结构体(task_struct)。这个函数会调用 copy_process
和 copy_thread
来执行具体的进程复制工作。
③dup_task_struct
负责分配新的任务结构体,并复制父进程的任务结构体信息。
④copy_process
是一个辅助函数,用于复制进程相关的信息,包括文件描述符、信号处理器、资源限制等。它会创建一个新的执行上下文,确保新进程拥有适当的资源和状态。
⑤copy_thread
也是一个辅助函数,用于复制进程的线程信息。这包括处理线程的调度、信号处理、寄存器状态等。copy_thread
确保新进程的线程拥有适当的状态。
⑥ret_from_fork
函数是一个汇编函数,它在进程创建后执行,负责将新进程的执行流程切换到用户空间。这个函数主要用于设置寄存器状态,以使新进程从创建点开始执行用户空间代码。
这些函数一起协作以实现新进程的创建,包括分配资源、复制父进程的状态和创建新的任务结构体。这是Linux内核用于多任务处理的核心机制,允许多个进程或线程同时运行在一个系统上。每个函数都有特定的职责,以确保新进程的正确初始化和执行。
以上就是fork函数的执行原理和过程。
附、chatGPT辅助问答:
1、
2、