fork()会创建一个新的子进程,该子进程是调用进程(父进程)的一个副本。新的进程与原进程共享相同的代码,但它们拥有独立的地址空间和资源。
1.头文件
#include <sys/types.h>
#include <unistd.h>
2.函数原型
pid_t fork(void);
3.参数
无。
4.返回值
成功创建子进程:在父进程中:返回子进程的 PID(进程 ID);在子进程中:返回 0。
创建进程失败:返回值小于 0,表示错误,通常由于系统资源不足或达到进程限制。
5.示例:使用fork()函数创建子进程
| #include <stdio.h>
#include <stdlib.h> #include <sys/types.h> #include <unistd.h> // 包含 fork() 和 getpid() 函数 #include <sys/wait.h> // 包含 wait() 函数 int main() { // 创建子进程 pid_t pid = fork(); if (pid < 0) { // 创建进程失败 perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程代码 printf("I am the child process. My PID is %d.\n", getpid()); exit(EXIT_SUCCESS); // 子进程正常退出 } else { // 父进程代码 printf("I am the parent process. My PID is %d, and my child's PID is %d.\n", getpid(), pid); // 等待子进程结束 wait(NULL); printf("Child process has terminated.\n"); } return 0; } |
6.运行结果
| I am the parent process. My PID is 200737, and my child's PID is 200738.
I am the child process. My PID is 200738. Child process has terminated. |
7.代码解析
pid_t pid = fork(); 创建一个新的子进程,这个子进程是父进程的副本,通过判断fock的返回值在父子进程中分别执行不同的代码。使用 wait() 函数可以确保父进程在子进程结束后继续执行,以避免资源泄漏,wait() 函数具体情况后面再做讲解。
8.fork的父子进程之间的资源继承情况
⚫共享代码段:代码段(文本段)是只读的,因此父进程和子进程可以共享同一个代码段。在内存中,代码段只存在一份,这样可以减少内存的浪费。父进程和子进程虽然执行相同的代码,但它们运行在不同的进程空间中,并拥有各自的上下文和数据。
⚫复制数据段、堆、栈:在 fork() 调用后,数据段、堆和栈部分的内容是完全复制的。尽管初始时子进程是父进程的副本,但它们的存储空间是独立的。这意味着子进程可以独立地修改它的数据段(全局和静态变量)、堆(动态分配内存)和栈(局部变量和函数调用堆栈)而不会影响父进程的相应部分,反之亦然。
⚫文件描述符的继承:子进程会继承父进程的文件描述符表,这意味着在子进程中打开的文件、管道、网络连接等都会继承下来。然而,父进程和子进程在文件描述符上操作时,它们的文件偏移量是共享的(除非在 fork() 之后关闭或重新打开),但对于每个进程的文件描述符表来说,文件描述符的使用是独立的。
⚫内存共享与分离,写时拷贝(Copy-on-Write, CoW):为了提高效率,现代操作系统实现了写时拷贝技术。当 fork() 被调用时,子进程并不会立即复制整个父进程的数据段、堆和栈,而是与父进程共享相同的内存页。只有当父进程或子进程尝试修改这些内存页时,操作系统才会进行实际的内存拷贝,创建独立的副本。这样可以显著节省资源,特别是在子进程没有对内存进行修改的情况下。
⚫fork()之后的执行:父进程和子进程会并发地执行 fork() 调用之后的代码,两者的执行顺序无法预测。可以通过 fork() 的返回值来区分父进程和子进程,在父进程中,fork() 的返回值是子进程的进程 ID(PID);在子进程中,fork() 返回 0;如果 fork() 调用失败,返回值是小于 0 的错误码。
⚫fork()函数具有并发执行:父进程和子进程几乎同时执行,输出的顺序无法保证。独立进程空间:父子进程共享代码段,但有各自独立的数据段、堆和栈,它们可以修改自己的数据,而不会影响彼此。写时拷贝:子进程只有在修改数据时,才会真正进行内存复制,从而减少不必要的资源消耗。通过这些特性,fork() 成为 Linux 系统中进程创建的核心函数。
196