一、相关知识
1. fork()函数的理解
一个进程调用fork()函数创建子进程时,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
fork()是在用户态用于创建一个子系统的系统调用。Fork()系统调用在父进程和子进程各返回一次。即:fork可以被调用一次,却能够返回两次,它可能有三种不同的返回值:
1.在父进程中,fork返回新创建子进程的进程ID;
2.在子进程中,fork返回0;
3.如果出现错误,fork返回一个负值;
在执行fork函数后,如果新进程创建成功,则返回两个进程,一个是父进程,一个是子进程。在父进程中,fork返回新创建子进程的进程ID;在子进程中,fork函数返回0。我们可以通过输出fork返回的值来判断当前进程是子进程还是父进程。
2.fork()函数的底层实现
Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:
1. 复制一个PCB——task_struct
err = arch_dup_task_struct(tsk, orig);
2. 要给新进程分配一个新的内核堆栈
ti = alloc_thread_info_node(tsk, node);
tsk->stack = ti;
setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈
3. 要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部
Linux通过clone()系统调用实现fork()。这个调用通过一系列的参数标志来指明父,子进程需要共享的资源。fork(),vfork()和__clone()库函数都根据各自需要的参数标志去调用clone()。然后由clone()去调用do_fork()。do_frok完成了创建中的大部分工作,它的定义在ker/frok.c文件中。该函数调用copy_process()的函数,然后让进程开始运行。copy_process()做扫尾工作并返回一个指向子进程的指针。再回到do_fork()函数,如果copy_process()函数返回成功,新创建的子进程被唤醒并让其投入运行。
二、实验过程
1.使用实验楼的虚拟机打开 shell
2. 下载新版本menu,在已经完成了的MenuOS里面增加一个命令fork,覆盖test.c文件
3.使用命令make rootfs进行编译,使之生效,观察到menu中多了一个fork命令,且可以正确执行
4.在shell1中启动内核
5.另开一个终端,在shell2中进行gdb调试
6.在sys_clone、do_fork、dup_task_struct、copy_process、copy_thread、ret_from_fork处设置断点
7.使用命令“c”继续执行,gdb中continue执行到断点do_fork处
8.继续调试,会停留在第3个断点,停在dup_task_struct函数,最终进入dup_task_struct内部,停留在copy_thread和ret_from_fork上