2.2.2 创建子进程
创建进程分叉
- 进程分叉将创建当前进程(
父进程
)的拷贝。进程分叉初始,父进程
与子进程
具有完全相同的状态:两者共享相同代码,运行到代码的相同位置,具有完全相同的数据集。当然,两者的进程标识pid
与ppid
是不同的。 返回值
:进程分叉将有两个返回值,一个来自父进程
,一个来自子进程
。- 在
父进程
中,返回子进程
的pid
。 - 在
子进程
中,返回0
。 - 如果进程创建异常,不会产生
子进程
,返回-1
。
- 在
#include <sys/types.h>
#include <unistd.h>
// create a child fork process
pid_t fork(void);
示例:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main() {
printf("Fork test:\n");
int pid = fork();
if (pid == -1) {
perror("fork");
return -1;
} else if (pid) {
// parent process
printf("This is parent process, pid = %d, ppid = %d\n", getpid(), getppid());
sleep(1);
} else {
// child process
printf("This is child process, pid = %d, ppid = %d\n", getpid(), getppid());
sleep(1);
}
for (int i = 1; i <= 3; i++) {
printf("%d\t%d\n", getpid(), i);
}
return 0;
}
输出:
Fork test:
This is parent process, pid = 10001, ppid = 1506 // 假设父进程 id 为 10001
This is child process, pid = 10086, ppid = 10001 // 假设子进程 id 为 10086
10001 1
10086 1
10001 2
10086 2
10086 3 // 无法预判父子进程谁先谁后
10001 3
注:由于fork
直译为分叉
,所以该进程创建又称为进程分叉。
进程分叉内存管理原理
fork
函数将进程进行拷贝,生成一个完全相同的进程。- 由于
fork
函数生成一个完全相同的进程,因此子进程
将与父进程
有相同的虚拟地址映射表。换言之,子进程
与父进程
的虚拟地址
映射到相同的物理地址
,但是两者在数据上应该具有独立性,这导致了子进程
与父进程
对当前数据只有读的权限,不具备写的权限。如果进程对物理地址
的值进行修改,将导致两者的数据同时发生修改,这不是期望发生的。 进程分叉
运用了写时拷贝
的技术,具备了写的权限,同时为进程分叉
节约了大量的时间与空间。在进程分叉
的初始状态,子进程
与父进程
的虚拟内存指向相同的真实内存空间,当子进程
与父进程
的任意一方需要修改时,修改部分的虚拟内存会指向一片新的真实内存,并赋予这个空间新的值。这样做的好处是,例如代码和一些无需更改的数据,将不会重复占用真实内存。
- 以上过程均由内核自动完成。
退出进程
- 进程退出的方法有三种:
_exit(0)
、exit(0)
(推荐)、main
函数中的return 0
。 main
函数中的return 0
实际上是调用了exit(0)
。exit
函数是标准C库
函数,是对Linux
函数_exit
的封装。
#include <unistd.h>
// exit a process
// status:
// exit status, 0~255
void _exit(int status);
#include <stdlib.h>
// exit a process
// status:
// exit status
void exit(int status);