linux进程——fork()函数
1、fork()函数简介
(1)函数原型
#include <sys/types.h>
#include<unistd.h>
pit_t fork(void)
(2)功能
- fork用于在已存在的进程中创建一个新的进程。
- 创建的新进程我们称之为子进程,原进程我们称为父进程。
(3)返回类型及返回值
fork()函数的返回类型为 pid_t 类型,这时基本系统数据类型,它的实际类型是一个int类型。
1)创建子进程成功,返回两次(父进程和子进程各返回一次,后面解释为什么返回两次):
- 子进程返回0
- 父进程返回新创建子进程的进程id(pid_t类型)。
2)创建子进程失败,返回-1。
2、fork 函数是如何创建一个子进程的
内核为子进程创建了一个新的task_struct结构。子进程几乎是父进程的克隆体,它将获得父进程数据空间、堆、栈、pc指针等资源的副本。
父子进程虽几乎一样,但是仍有些差别,例如
- process id 进程ID
- 父进程ID
- 子进程ID 等
3、父子进程的具体在内存中的结构如下图所示
可以看出,父进程和子进程在物理内存中的代码段是共享的,但是堆栈内存是独立的( 图中只画出了栈内存,但是堆内存也是独立的)。
4、调用fork函数一次,为什么返回两次?
(1)返回两次的原因是:
子进程复制时复制了父进程的堆栈段,也包括pc指针,而pc指针就是正要执行的程序,所以两个进程都停留在fork函数中,等待返回。因此fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的(fork 的两次返回是内核帮我们实现的)。
(2)两次返回的区别
fork被调用一次,但返回两次,子进程和父进程各返回一次,并且子进程里返回值是0,而在父进程里其返回值是子进程的进程ID。
(3)两次返回有区别的原因
函数返回时pid所在栈段被复制并填写了不同的返回值,也就是两个进程拥有不同的堆栈区,函数执行完成后,返回到各自栈区的值是不同的。
5、fork出来的子进程,它和父进程执行的先后顺序?
我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的(schedule算法,即内核调度算法)实现。
- 如果父进程先运行完,那么子进程就会变成孤儿进程。此时系统会遍历属于回收的父进程的子进程,并将这些进程交由init进程 来接管,init进程就成为它的父进程。
- 如果子进程,在父进程结束前先结束,且父进程一直不退出。那么子进程就会变成僵尸进程。
6、 我们在写程序时,父进程死掉后,一般不让它退出,为什么?
因为父亲死了,init进程就将会成为其子进程的父亲。为了避免init被塞进太多的进程,则需要父进程不退出。
7、 为什么需要僵尸状态?
因为子进程的退出状态很重要,它可以返回子进程的死亡状态。因为,只有回收了子进程的退出状态,才能清理僵尸状态进程的资源(代表进程的struct 结构)。
8、新建一个进程的函数为什么起名为fork?
fork 单词的中文意思是叉子,而新创建一个进程,就像是一个叉子。如下图: