fork之后多了一个子进程,但是是怎么做到的呢?
进程 = 内核数据结构 + 进程的代码和数据
以下回答部分来自GPT,可以看一下是否解决了你的困惑:
在操作系统中,fork
是一个创建子进程的系统调用。当执行 fork
时,操作系统会做以下几个步骤:
复制父进程:操作系统会复制父进程的内存空间、代码和数据。这包括进程的代码段、数据段、堆和栈等。
设置进程标识符:操作系统为子进程分配一个唯一的进程标识符(PID),用于在操作系统中标识和管理该进程。
设置父子关系:子进程的父进程会被设置为调用 fork
的进程(即父进程)。这样可以建立起父子进程之间的关系。
返回值:对于父进程,fork
调用返回子进程的 PID;对于子进程,fork
调用返回 0。这样可以让父子进程在执行后续操作时有不同的逻辑分支。
通过上述步骤,操作系统成功地创建了一个完全独立的子进程。子进程与父进程共享相同的代码和数据,但是它们有各自独立的内存空间和资源。
接下来,父子进程可以并行地执行各自的代码。它们通过操作系统提供的进程调度机制在操作系统内核中进行切换,以便让每个进程都获得适量的执行时间。
需要注意的是,fork
仅创建了一个子进程的初始副本。子进程之后可以通过调用 exec
系统调用来加载新的程序代码和数据,从而替换它自己的内存空间。这样,子进程就可以执行其他的程序了。
该回答引用NewBing,仅供参考,不保证完全正确
当您调用fork()函数时,操作系统会为子进程创建一个新的PCB(进程控制块),并将父进程的PCB的副本复制到子进程的PCB中。子进程几乎拷贝了父进程的全部内容,包括堆、栈、代码段等。因此,子进程的栈是从父进程那拷贝过来的,它和父进程的函数栈帧就是完全相同的,在函数执行完毕后,父子进程在各自的函数栈帧中返回,造成了我们所看到的两次返回的假象。²³
如果您想了解更多关于fork()函数和操作系统如何处理它的信息,这里有一些参考资料可以帮助您更好地理解:
源: 与必应的对话, 2023/7/12
(1) fork到底做了什么?_父子进城谁先执行_Dxx_xx4的博客-CSDN博客. https://blog.csdn.net/Dxx_xx4/article/details/89048283.
(2) Linux fork创建子进程 - 管理员D - 博客园. https://www.cnblogs.com/electronic/p/10932717.html.
(3) Linux fork创建子进程 - 管理员D - 博客园. https://bing.com/search?q=fork+%e5%88%9b%e5%bb%ba%e5%ad%90%e8%bf%9b%e7%a8%8b+OS%e5%81%9a%e4%ba%86%e4%bb%80%e4%b9%88.
(4) fork()函数介绍和创建多个子进程_zhcblog的博客-CSDN博客. https://blog.csdn.net/qq_43479736/article/details/107130564.
(5) python3启动子进程之 os.fork() - 运维时代 - 博客园. https://www.cnblogs.com/lijinlei521/p/12699388.html.
(6) 进程系统调用——fork函数深入理解(代码演示) - 知乎. https://zhuanlan.zhihu.com/p/422928238.
在操作系统中使用fork函数创建子进程时,操作系统会做以下工作:
为子进程创建一个独立的进程控制块(PCB),其中包含了子进程的状态信息和其他相关数据。可以使用数据结构task_struct
来表示PCB。
将父进程的PCB中的数据(包括代码、数据等)复制到子进程的PCB中,使得子进程可以具有父进程的所有数据。可以使用系统调用fork()
来完成这个步骤。
子进程和父进程之间的内存空间是相互独立的,因此需要为子进程分配一个新的用户空间。可以通过使用虚拟内存的技术来实现这一步骤,其中包括复制页表和映射新的物理内存。
子进程的文件描述符表和父进程的文件描述符表是独立的,因此需要为子进程创建一个新的文件描述符表。可以通过复制父进程的文件描述符表来实现这一步骤,使得子进程可以访问相同的文件。
子进程需要有一个新的进程ID(PID),以便可以在操作系统中唯一标识出子进程。操作系统会为子进程分配一个新的PID,并将其保存在子进程的PCB中。
接下来,操作系统会将子进程的状态设置为就绪态,并将其添加到就绪队列中,等待被调度执行。
父进程和子进程可以分别执行不同的代码,但是它们之间可以通过进程间通信机制来进行相互通信和传递数据。
以上是在操作系统中使用fork函数创建子进程时,操作系统所做的工作。当父进程调用fork函数时,会返回子进程的PID给父进程,而子进程返回0给自己,以便区分父进程和子进程的执行路径。