嵌入式解谜日志之Linux操作系统—进程2
其核心思想是多个调用者最初共享同一份资源,只有在某个调用者试图修改资源时(code,data,heap,stack),系统才会真正复制一份副本给该调用者(子进程)。②进程是动态的,程序执行的过程,包括进程的创建,调度(cpu利用时间片轮转,平级),消亡。0-3G是进程的空间(大小3G),3G-4G的内核的空间(大小1G),都是虚拟空间。1.内核主要的功能之一就是完成进程调度,硬件,bios,io,
进程(process):
(1)进程的定义:正在进行的程序,会去分配内存资源(mem),cpu的调度 ,(flash ssd:固态硬盘)目的:为了实现并发,同一时刻执行多任务
(2)pcb:进程控制块(process control block)结构体
①Pid:进程标识符 身份标识。
②Pwd:当前工作路径:通过chdir设置
③umask 0002 :控制新文件的权限,与新建文件和新建目录有关
④进程打开的文件列表
⑤信号相关的设置,处理异常的io
⑥资源的上限ulimit:(用命令ulimit -a查看,可显示资源上限)
栈的大小(stack size):8k
打开的文件大小(open file):1024
(3)进程和程序的区别:
1.存在时间:①程序存在于./a.out之前
②进程存在于./a.out之后
2.存在状态:①程序是静态的,存储在硬盘的代码,数据集合。
②进程是动态的,程序执行的过程,包括进程的创建,调度(cpu利用时间片轮转,平级),消亡。
(.c ---->a.out---->process(pid))
1)程序是永存的,进程是暂时的
2)进程有程序的状态变化,程序没有
3)进程可以并发,程序没有并发
4)进程与进程之间会存在竞争计算机的资源
5)一个程序可以运行多次,变成多个进程,一个进程可以运行一个或多个程序
3.内存的分布:0-3G是进程的空间(大小3G),3G-4G的内核的空间(大小1G),都是虚拟空间
虚拟地址:物理内存和虚拟内存的地址 映射表1page=4k (mmu管理)

4.进程的分类:
①交互式进程
②拟处理进程:shell脚本
③守护进程:在特定的条件自动触发
(4)进程的状态:
1.基本操作系统:①就绪:等待cpu调度
②运行态:cpu的调度运行中
③阻塞(等待,睡眠):因为某些条件不满足,就会发生阻塞(条件满足后重新进入就绪状态)
2.Linux下的状态:运行态,睡眠态,僵尸态,暂停态
(5)进程的调度:进程上下文切换
内核通过进程,执行指令,指令通过编译成为“机器语言”,最终由cpu来完成
cpu是进程指令的唯一执行者
调度核心:cpu利用时间片轮转
并发vs并行
①并发:单cpu上快速切换执行多个任务(微观串行,宏观并行)
②并行:多个cpu同时执行多个任务(真正同时运行)
1.内核主要的功能之一就是完成进程调度,硬件,bios,io,文件系统,驱动
2.调度算法:other,idle,rr,fifo
①先来先服务
②短任务优先
③多级任务队列
(6)查询进程相关命令:
1.ps aux:查看进程相关信息
| 状态符 | 含义 | 说明 |
|---|---|---|
| R | 运行/就绪态 | 正在CPU执行或等待调度 |
| S | 可中断睡眠态 | 等待事件(可被信号唤醒) |
| D | 不可中断睡眠态 | 等待I/O完成(不可唤醒) |
| T | 暂停态 | 被信号停止(如SIGSTOP) |
| Z | 僵尸态 | 进程已终止但资源未回收 |
| (无) | 结束态 | 资源完全回收 |
进程的调度与上下文切换
2.top:根据CPU占用率查看进程相关信息(退出用q键)
3.kill /killall- 发送一个信号,终止进程
①kill PID # 正常终止进程
②kill -9 PID # 强制杀死进程
③kill -15 PID # 优雅终止进程
④kill -l # 查看所有信号
⑤kill -2 PID :发送一个信号+PID对应的进程,默认接收者关闭
⑥killall -9 a.out:终止所有的a.out
进程原语:
(1)fork():产生一个子进程(父进程的副本)//并发
pid_t ret=fork();
1.特性:
①一次调用,返回两次(父和子)---从fork后开始程序至少执行两次(父子分开执行)
ret>0:执行父进程,ret代表子进程的id号
ret==0:执行子进程
②父进程和子进程执行顺序不确定(如果非要确定哪个要先运行,需要IPC机制(内存功共享)。)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
pid_t ret = fork(); // 创建子进程,返回两次
if(ret > 0) // 父进程分支(ret = 子进程PID)
{
while(1) // 父进程无限循环
{
printf("father,发视频....\n");
sleep(1); // 暂停1秒控制输出频率
}
}
else if(0 == ret) // 子进程分支(ret = 0)
{
while(1) // 子进程无限循环
{
printf("child,接收控制....\n");
sleep(1);
}
}
else // fork失败
{
perror("fork err\n");
return 1;
}
return 0;
}
理想运行结果:
father,发视频…
child,接收控制…
father,发视频…
child,接收控制…(交替输出,顺序取决于调度)
③变量隔离:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int a = 20; // 全局变量
int main()
{
pid_t ret = fork();
if(ret > 0) // 父进程
{
sleep(3); // 确保子进程先修改
printf("father ,a is %d\n", a); // 输出20
}else if(0 == ret) // 子进程
{
a += 10; // 修改副本
printf("child ,a is %d\n", a); // 输出30
}
printf("a is %d\n", a); // 父进程输出20,子进程输出30
return 0;
}
理想运行结果:
child ,a is 30
a is 30
father ,a is 20
a is 20因为子进程在fork()复制完父进程的数据后,两者单独运行,数据不共享
(2)getpid()和getppid()
1.getpid():获得当前进程的id号
pid_t getpid(void);
2.getppid():功能:获得当前进程的父进程id号
pid_t getppid(void)
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t ret = fork();
if(ret > 0) // 父进程
{
sleep(3);
printf("father ,pid:%d ppid:%d\n", getpid(), getppid());
}
else if(0 == ret) // 子进程
{
printf("child ,pid:%d ppid:%d\n", getpid(), getppid());
}
printf("pid:%d ppid:%d\n", getpid(), getppid());
return 0;
}
理想运行结果:
child ,pid:26205 ppid:26204
pid:26205 ppid:26204
father ,pid:26204 ppid:3199
pid:26204 ppid:3199
(3199为终端父进程ID)
(3)多重fork()
#include <stdio.h>
#include <unistd.h>
int main()
{
fork() && fork() || fork(); // 逻辑运算符触发5个进程(注意逻辑运算的截断)
printf("pid:%d\n", getpid());
return 0;
}

当&&左边为0时,右边被截断
进程关系:
1个父进程 → 生成4个子进程(含1个孙进程)
理想运行结果:
pid:1001
pid:1002
pid:1003
pid:1004
pid:1005
(共5行输出,PID顺序不定)
(7)父子进程的关系:
1.子进程是父进程的副本(复制品)。子进程获得父进程的代码段和数据段,堆,栈,正文段共享。
写时复制:其核心思想是多个调用者最初共享同一份资源,只有在某个调用者试图修改资源时(code,data,heap,stack),系统才会真正复制一份副本给该调用者(子进程)。
2.关键区别:
①fork()返回值不同(父进程>0,子进程=0)
②PID/PPID不同
③资源计数器重置(如文件描述符)
3.执行起点:子进程从fork()后开始执行
4.典型场景:
①网络服务:父子进程执行相同代码
②程序替换:一个进程需要执行一个不同的程序:fork() + 【exec()执行新程】
(8)进程的终止:8个情况

(9)exit()和_exit():指终止程序运行的函数或命令
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello"); // 无换行符,缓冲区未刷新
// exit(0); // 会刷新缓冲区 → 输出"hello"
_exit(0); // 不刷新缓冲区 → 无输出
return 0;
}
1.关键区别:
①exit(0):先刷新I/O缓冲区,再执行清理函数,最后调用exit()
②_exit(0):立即终止,不处理缓冲区和清理函数
2.理想结果:①用
exit(0)时输出hello②用
_exit(0)时无输出
(10)进程的退出:
1.孤儿进程:父进程先消亡,子进程就是孤儿进程,子进程自动由init接管
init回收机制原理
- 当进程终止时,内核会检查其是否有子进程仍在运行
- 内核将这些子进程的PPID改为1
- init进程定期调用
wait()系统调用回收已终止的子进程 - 资源(如内存、文件描述符等)被系统回收
int main()
{
pid_t ret = fork();
if(ret > 0) // 父进程
{
sleep(3);
exit(0); // 父进程提前终止
}
else // 子进程
{
while(1) {
printf("child,接收控制....pid:%d ppid:%d\n", getpid(), getppid());
sleep(1);
}
}
return 0;
}
现象:子进程PPID变为1(
init进程ID)
2.僵尸进程:子进程先消亡,就是僵死进程,内存和代码被释放,且再也不会被内核调用,但是子进程的pcb(进程控制块)依然存在在内核空间里,需要父进程调用wait(),waitpid()获得子进程的退出状态,并释放子进程的pcb块
子进程变为僵尸进程(Zombie),状态为 Z,仅保留 PCB 等待父进程处理。
若父进程不回收,僵尸进程会一直占用 PID 和 PCB 资源,最终可能导致系统资源耗尽。
int main()
{
pid_t ret = fork();
if(ret > 0) // 父进程
{
while(1) {
printf("father,发视频....pid:%d ppid:%d\n", getpid(), getppid());
sleep(1);
}
}
else // 子进程
{
sleep(3);
exit(0); // 子进程终止,但父进程未回收 → 僵尸
}
return 0;
}
现象:
ps aux中子进程状态为Z,资源未释放
资源回收:僵尸进程需父进程调用wait()/waitpid()回收;孤儿进程由init自动回收
(11)进程回收:
父进程通过
wait/waitpid回收子进程资源,避免僵尸进程。核心功能包括:
- 阻塞等待子进程退出
- 获取子进程退出状态
- 释放内核 PCB 资源
回收全过程:子进程先退出(exit())--->内核管理子进程的状态(将状态标记为”Z“)--->发信号给父进程(上面)---->调用wait()/waitpid()(获取内核中子进程退出状态)--->通知内核释放子进程pcb块
二、wait() 和 waitpid() 的核心作用这两个系统调用的本质是:
让父进程从内核中获取子进程的退出状态,并通知内核释放子进程的 PCB 资源。
三、工作原理详解
1. 内核中的子进程状态管理
子进程终止后,内核会做两件事:
- 将子进程状态标记为
Z(僵尸态); - 在父进程的 PCB 中记录 “有子进程终止待处理” 的信息(如通过信号
SIGCHLD通知父进程)。
此时,子进程的退出状态(exit status)被暂存在内核中,等待父进程读取。
1.wait:
(父进程等待子进程回收)由父进程调用,回收子进程的pcb会发生阻塞,父进程回收资源的时候,子进程没有退出,父进程就需要要等待子进程结束后回收
pid_t wait(int *status);
status参数是一个整型指针,用于存储子进程的退出状态信息。
原理步骤:
- 父进程调用
wait()后,内核检查是否有子进程处于僵尸态:- 若有:立即返回该子进程的 PID,并将退出状态写入
status指针指向的内存;同时内核释放该子进程的 PCB 资源。 - 若无:父进程进入阻塞状态(暂停运行),直到有子进程终止后被内核唤醒,重复步骤 1。
- 若有:立即返回该子进程的 PID,并将退出状态写入
- 若父进程没有子进程(或所有子进程已被回收),
wait()立即返回-1,并设置errno为ECHILD。
- 阻塞调用进程,直到任意一个子进程终止。
- 无法指定等待的子进程,只能等待第一个终止的子进程。
-
返回终止子进程的 PID,并通过参数获取退出状态。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if(pid > 0) // 父进程
{
printf("father pid:%d, child pid:%d\n", getpid(), pid);
pid_t recycle_pid = wait(NULL); // 阻塞等待任意子进程
printf("recycle end... pid:%d\n", recycle_pid);
}
else if(0 == pid) // 子进程
{
int i = 3;
while(i--)
{
printf("i'm processing...\n");
sleep(1); // 模拟工作
}
exit(0); // 正常退出
}
else
{
perror("fork");
return 1;
}
return 0;
}
理想输出:
father pid:12345, child pid:12346
i'm processing...
i'm processing...
i'm processing...
recycle end... pid:12346
- 关键点:
- 父进程在
wait(NULL)处阻塞直到子进程退出 - 子进程退出后父进程立即回收资源
- 父进程在
2.waitpid
pid_t waitpid(pid_t pid, int *status, int options);
-
可以指定等待的子进程 PID(通过
pid参数),或一组子进程(通过pid的特定值)。 -
支持非阻塞模式(通过
options参数设置WNOHANG)。 - 更灵活,适用于需要精确控制子进程等待的场景。
参数说明
waitpid 的 pid 参数
pid > 0:等待指定 PID 的子进程。-
pid = -1:等待任意子进程(等效于wait)。 pid = 0:等待与调用进程同进程组的任意子进程。pid < -1:等待进程组 ID 为|pid|的任意子进程。
options 参数
WNOHANG:非阻塞模式,如果没有子进程终止,立即返回 0。 WUNTRACED:返回已停止(未终止)的子进程状态(需配合信号使用)。
优势:父进程可在等待子进程的同时处理其他任务,避免长时间阻塞
4. 退出状态的解析(status 参数)
wait()/waitpid() 通过 status 指针返回子进程的退出状态(32 位整数),需用宏解析:
WIFEXITED(status):判断子进程是否正常退出(调用exit()或main()返回);WEXITSTATUS(status):若正常退出,获取退出码(exit(n)中的n);WIFSIGNALED(status):判断子进程是否被信号杀死;WTERMSIG(status):若被信号杀死,获取终止信号的编号
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if(pid > 0) // 父进程
{
printf("father pid:%d, child pid:%d\n", getpid(), pid);
int status;
while(1)
{
// 非阻塞等待指定子进程
pid_t recycle_pid = waitpid(pid, &status, WNOHANG);
if(pid == recycle_pid) // 回收成功
{
printf("recycle end... pid:%d\n", recycle_pid);
if(WIFEXITED(status))
{
printf("child ret value is %d\n", WEXITSTATUS(status));
}
break;
}
else if (0 == recycle_pid) // 子进程未结束
{
printf("子进程未结束...\n");
usleep(500000); // 休眠 0.5 秒
}
else
{
printf("wait error\n");
break;
}
}
}
else if(0 == pid) // 子进程
{
int i = 10;
while(i--)
{
printf("i'm processing...pid:%d\n", getpid());
sleep(1);
}
exit(20);
}
else
{
perror("fork");
return 1;
}
return 0;
}
理想输出:
father pid:12345, child pid:12346
子进程未结束...
i'm processing...pid:12346
...(父进程持续打印"子进程未结束",子进程循环 10 次)...
i'm processing...pid:12346
recycle end... pid:12346
child ret value is 20
- 关键点:
- 父进程通过
WNOHANG实现非阻塞轮询 - 子进程运行期间父进程可执行其他任务(此处仅打印提示)
- 父进程通过
5.注意事项
- 如果调用进程没有子进程,
wait会立即返回错误(ECHILD)。 - 使用
waitpid时,若指定的pid不存在或无权访问,会返回错误。 - 僵尸进程(已终止但未
wait的进程)会占用系统资源,应确保及时回收。
| 函数 | 特点 |
|---|---|
wait(&status) |
阻塞等待任意子进程退出,等效于 waitpid(-1, &status, 0) |
waitpid(pid, ...) |
可指定回收目标进程,支持非阻塞模式 (WNOHANG) |
四、wait() 与 waitpid() 的区别
| 特性 | wait() |
waitpid() |
|---|---|---|
| 等待范围 | 只能等待任意子进程终止 | 可通过 pid 指定特定子进程 |
| 阻塞行为 | 始终阻塞 | 可通过 WNOHANG 设为非阻塞 |
| 返回值 | 终止的子进程 PID(失败返回 -1) | 同左,但非阻塞时无子进程终止返回 0 |
| 适用场景 | 简单场景(等待任意子进程) | 复杂场景(指定子进程、非阻塞等待) |
一、子进程的正常结束流程
子进程的正常结束是主动退出(区别于被信号强制终止),通常由自身逻辑触发,流程如下:
1. 触发退出
子进程通过以下方式主动终止:
- 调用标准库函数
exit(status):
执行用户态清理工作(如刷新标准 I/O 缓冲区、调用atexit注册的退出处理函数),然后进入内核态。 - 调用系统调用
_exit(status)或exit_group(status):
直接进入内核态,不执行用户态清理(如不刷新缓冲区),更高效。 - 主函数
main()执行完毕并返回:
等价于exit(main的返回值),会隐式触发exit()流程。
注:status 为退出状态码(0 表示正常,非 0 表示异常),将被传递给父进程。
2. 内核处理子进程终止
子进程进入内核态后,内核执行以下操作:
- 释放用户态资源:
回收子进程的代码段、数据段、堆、栈等内存,关闭所有打开的文件描述符(包括文件、管道、套接字等)。 - 保留内核态信息:
不立即释放进程控制块(PCB),而是保留退出状态(status或终止信号)、进程 ID(PID)等信息,供父进程查询。 - 标记为僵尸进程:
子进程状态变为Z(Zombie),此时进程已停止运行,但 PCB 仍占用内核资源。 - 通知父进程:
向父进程发送SIGCHLD信号(默认忽略,需父进程显式处理或主动等待)。
二、父进程的正常结束流程(含回收子进程)
父进程的正常结束需先确保所有子进程资源被回收(避免僵尸进程),再自身退出,流程如下:
1. 回收子进程资源
父进程必须通过 wait() 或 waitpid() 回收子进程的退出状态,彻底释放其 PCB 资源:
- 阻塞等待(
wait()或waitpid(pid, &status, 0)):
父进程暂停运行,直到子进程终止,获取其退出状态后,内核释放子进程 PCB。 - 非阻塞轮询(
waitpid(pid, &status, WNOHANG)):
父进程定期检查子进程是否终止,不阻塞自身运行,适用于需要同时处理其他任务的场景。 - 信号驱动(捕获
SIGCHLD信号):
子进程终止时,父进程收到SIGCHLD,在信号处理函数中调用waitpid()回收资源,无需主动轮询。
2. 父进程自身退出
回收所有子进程后,父进程通过 exit() 或 main() 返回结束自身:
- 执行用户态清理(如
exit()的缓冲区刷新); - 内核释放父进程的所有资源(内存、文件描述符、PCB 等);
- 若父进程有自己的父进程(如 shell),其退出状态会被上层父进程回收。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// 子进程:执行任务后正常退出
printf("子进程 %d 执行完毕,准备退出\n", getpid());
exit(0); // 主动退出,状态码0
} else if (pid > 0) {
// 父进程:回收子进程
int status;
pid_t exited_pid = waitpid(pid, &status, 0); // 阻塞等待
if (exited_pid == pid) {
if (WIFEXITED(status)) {
printf("父进程回收子进程 %d,退出码:%d\n",
pid, WEXITSTATUS(status));
}
}
// 父进程自身退出
printf("父进程 %d 执行完毕,退出\n", getpid());
exit(0);
}
return 0;
}
子进程 父进程
| |
|-- 执行任务 |-- 创建子进程后继续执行
| |
|-- 调用 exit(0) 触发退出 |
| → 清理用户态资源 |
| → 进入内核态 |
| → 内核释放用户资源 |
| → 标记为僵尸进程(Z状态) |
| → 发送 SIGCHLD 给父进程 |-- 收到信号或主动调用 waitpid()
| | → 读取子进程退出状态
| | → 内核释放子进程PCB
| |
|(已终止) |-- 完成回收,继续执行自身任务
| |
|-- 调用 exit(0) 退出
| → 内核释放父进程所有资源
五、总结
正常结束流程的核心是 “子进程主动退出→内核保留状态→父进程回收资源→父进程自身退出”
回调函数:atexit
int atexit(void (*function)(void));
参数:function:函数指针,指向void返回值void参数的函数指针
功能:注册进程退出前执行的函数
关键特性
- 注册的函数在程序正常终止(如
main返回或调用exit)时执行。 - 注册顺序为LIFO,最后注册的函数最先执行。
- 至少支持32个函数的注册(具体数量由实现定义)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE* fp;
char* p;
// 清理函数:释放资源并打印信息
void clean()
{
printf("clean fun, p is %s\n", p); // 打印字符串内容
fclose(fp); // 关闭文件
free(p); // 释放堆内存
}
int main(int argc, char **argv)
{
atexit(clean); // 注册清理函数
fp = fopen("1.txt", "r"); // 打开文件
p = malloc(50); // 分配堆内存
strcpy(p, "hello"); // 填充字符串
printf("process will end..\n"); // 主程序结束提示
return 0; // 触发 atexit 注册的 clean 函数
}
(12)exec族:替换当前进程的代码段和数据段,执行新程序。不创建新进程,仅替换执行内容。
exec族:- 替换当前进程执行新程序(代码段/数据段被覆盖)
- 常用于子进程启动新任务(如 shell 执行命令)

关键特性
- 成功时无返回:新程序从入口点开始执行,原进程代码段被完全替换
- 失败返回
-1:仅当文件不存在或权限不足等错误时返回 - 环境变量:
execlp/execvp使用当前进程的PATH环境变量查找可执行文件- 未指定路径时优先在
PATH指定目录搜索#include <stdio.h> #include <unistd.h> int main() { // 方式1:execl(需完整路径) // execl("/bin/ls", "ls", "-a", "-l", "--color=auto", NULL); // 方式2:execlp(自动按 PATH 查找) // execlp("ls", "ls", "-a", "-l", "--color=auto", NULL); // 方式3:execv(需完整路径 + 参数数组) char *const args[] = {"ps", "aux", NULL}; // execv("/bin/ps", args); // 方式4:execvp(自动按 PATH 查找 + 参数数组) execvp(args[0], args); // 仅当 exec 失败时执行 printf("看见就错了\n"); return 0; }代码运行理想结果
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 169160 12344 ? Ss Jul09 0:02 /sbin/init ...(完整 ps aux 输出)... - 关键点:
execvp成功执行ps aux命令- 不会打印
看见就错了(因exec成功时进程被替换) - 若命令不存在(如
execvp("invalid", NULL)),则返回-1并执行printf
(13)fork()+exec():创建并运行新进程
fork() 单独使用时,子进程会复制父进程的代码;而 exec 单独使用时,会替换当前进程。实际中通常先 fork() 创建子进程,再在子进程中调用 exec 加载新程序,实现 “创建一个全新的进程运行新程序” 的效果。
典型流程
- 父进程调用
fork()创建子进程(子进程是父进程的副本); - 子进程调用
exec系列函数,将自身替换为新程序; - 父进程继续执行原有逻辑(通常会调用
wait()回收子进程)。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid == -1) {
perror("fork 失败");
return 1;
}
if (pid == 0) {
// 子进程:执行新程序(ls -l)
printf("子进程 PID:%d,即将执行 ls -l\n", getpid());
char *args[] = {"ls", "-l", NULL};
execvp("ls", args); // 替换子进程为 ls 程序
perror("execvp 失败"); // 若失败才会执行
return 1;
} else {
// 父进程:等待子进程结束
printf("父进程 PID:%d,等待子进程 %d 完成\n", getpid(), pid);
wait(NULL); // 回收子进程
printf("子进程执行完毕\n");
}
return 0;
}
四、核心区别与联系
| 维度 | fork() |
exec 系列 |
|---|---|---|
| 功能 | 创建子进程(复制当前进程) | 替换当前进程为新程序(不创建新进程) |
| PID 变化 | 子进程有新 PID | PID 不变(仍是原进程) |
| 返回值 | 父进程返回子进程 PID,子进程返回 0 | 成功不返回,失败返回 -1 |
| 资源处理 | 复制父进程资源(写时复制优化) | 释放原进程代码 / 数据,加载新程序 |
| 典型用途 | 创建并行执行的进程 | 在现有进程中运行新程序 |
| 依赖关系 | 常与 exec 配合使用 |
通常在 fork() 后的子进程中调用 |
五、总结
fork()是 “复制进程”,创建一个与父进程几乎相同的子进程,用于实现并行执行;exec是 “替换进程”,用新程序覆盖当前进程的代码和数据,用于在进程中运行新程序;- 二者结合是 Unix 系统创建新进程运行新程序的标准模式(如 shell 执行命令时,先
fork再exec)。
更多推荐



所有评论(0)