进程(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回收机制原理

  1. 当进程终止时,内核会检查其是否有子进程仍在运行
  2. 内核将这些子进程的PPID改为1
  3. init进程定期调用wait()系统调用回收已终止的子进程
  4. 资源(如内存、文件描述符等)被系统回收
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参数是一个整型指针,用于存储子进程的退出状态信息

原理步骤

  1. 父进程调用 wait() 后,内核检查是否有子进程处于僵尸态:
    • 若有:立即返回该子进程的 PID,并将退出状态写入 status 指针指向的内存;同时内核释放该子进程的 PCB 资源。
    • 若无:父进程进入阻塞状态(暂停运行),直到有子进程终止后被内核唤醒,重复步骤 1。
  2. 若父进程没有子进程(或所有子进程已被回收),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 加载新程序,实现 “创建一个全新的进程运行新程序” 的效果。

典型流程
  1. 父进程调用 fork() 创建子进程(子进程是父进程的副本);
  2. 子进程调用 exec 系列函数,将自身替换为新程序;
  3. 父进程继续执行原有逻辑(通常会调用 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)。
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐