Linux---进程(九)进程控制

一、fork初识

fork 知识点总结(极简版)

  1. 作用
  • 创建一个新的子进程

  • 调用一次,分裂成两个进程(父 + 子)

  1. 返回值规则(最重要)
  • 父进程:返回 子进程 PID(>0)

  • 子进程:返回 0

  • 创建失败:返回 -1

  1. 执行特点
  • 父子进程代码相同,从 fork 之后同时往下执行

  • 谁先运行不确定(由系统调度决定)

  • 子进程会复制父进程的地址空间(数据、栈、堆等)

  • 现代系统用写时复制(Copy-on-Write),不立刻全复制

  1. 常见考点

  2. fork 之后父子谁先跑不一定

  3. 子进程会继承:文件描述符、当前目录、信号处理方式等

  4. 子进程有自己的:PID、PPID、计时器

  5. 父子退出互不影响,但父进程不 wait 可能产生僵尸进程

  6. 一句话记忆

父返子PID,子返0,调用一次,两个进程。

二、写时拷贝

  1. 是什么

fork 创建子进程时,不立刻复制整个内存,而是让父子共享同一份物理内存,只有当要修改数据时,才真正复制一页。

  1. 为什么要用
  • 传统 fork:直接复制全部数据,慢、浪费内存

  • COW:推迟复制、减少开销、提高效率

  1. 核心机制

  2. fork 后,父子虚拟地址不同,但指向同一块物理内存

  3. 内存页设为 只读

  4. 谁要写,就触发缺页异常

  5. 操作系统这时才真正复制那一页,再让进程修改

  6. 特点

  • 读时共享,写时复制

  • 只复制要修改的页,不复制全部

  • 让 fork 变得更快、更省内存

  • 是 Linux 实现 fork 的标准方式

  1. 一句话记忆

fork 不复制,共享只读;谁写谁复制,一页一复制。

三、进程终止

进程终止会有几种情况呢?

(1)代码跑完,结果正确。

(2)代码跑完,结果不正确。

(3)代码没跑完,进程异常了。

return 0其实是进程的退出码。

进程终止的本质是释放系统资源,就是释放进程申请的相关内核数据结构和对应的数据和代码。

进程退出状态

  1. 进程退出的两种情况
  • 正常退出:代码正常跑完,执行 return 0 或 exit(0) 。
  • 此时 signumber = 0 (表示没有被信号终止)
  • 退出码 exit_code 有意义(通常是 return 的值)
  • 异常终止:进程在运行中收到信号(如 SIGKILL 、 SIGSEGV )被强制终止。
  • 此时 signumber ≠ 0 (表示被信号终止)
  • return 语句根本没机会执行,因此退出码没有意义
  1. 进程状态的表示

进程执行的最终结果,可以用两个整数完整描述:

  • int sig :终止信号编号( 0 表示正常退出)
  • int exit_code :退出码(仅在 sig = 0 时有意义)

在 Linux 中,父进程通过 wait() 或 waitpid() 获取的 status 变量,就是这两个信息的组合体,需要用宏(如 WIFEXITED , WEXITSTATUS , WIFSIGNALED , WTERMSIG )来解析。

  1. 关键结论
  • return 没被执行 → 进程是被信号终止的,退出码无意义。
  • signumber = 0 → 进程正常结束,退出码有效。
  • signumber ≠ 0 → 进程被信号杀死,退出码无效。

两种正常的退出方式

1. 两种正常退出方式(不考虑信号等异常)

  1. 在 main 函数中使用 return
  • 这是最标准的方式。 main 函数的返回值会作为进程的退出码(exit code)。

  • 例如 return 0; 表示程序成功执行。

  1. 在程序的任意位置调用 exit() 函数
  • 这个函数会直接终止整个进程,无论它在哪个函数里被调用。

  • 它的参数就是进程的退出码,例如 exit(EXIT_SUCCESS); 。

**2. 关键区别(面试常考)

  • return 在非 main 函数中:只会结束当前函数,进程继续运行。
  • exit() 在任何函数中:都会直接结束整个进程。
  • main 函数的 return :等价于调用 exit() ,会终止进程。**
  1. 与进程状态的关系
  • 无论是 return 还是 exit() ,进程都是正常退出,此时 signumber = 0 ,退出码( exit_code )有效。

  • 如果进程是被信号(如 SIGKILL )终止的,那么 return 和 exit() 都不会执行, signumber ≠ 0 ,退出码也无意义。

非main函数,return ,函数结束。非main函数,exit,进程结束。

四、进程等待

一、作用(为什么要用)

  1. 让父进程等待子进程退出

  2. 回收子进程的PCB资源,避免僵尸进程

  3. 获取子进程的退出状态

二、wait() 函数

头文件

#include <sys/wait.h>

函数原型

pid_t wait(int *wstatus);

功能

  • 阻塞等待任意一个子进程退出

  • 子进程退出后,回收它

  • 把退出状态写到 wstatus 指向的位置

返回值

  • 成功:返回退出的子进程 PID

  • 失败:返回 -1(比如没有子进程)

特点

  • 阻塞:不等到子进程退出就不返回

  • 等待任意子进程

  • 无法指定等待某个子进程

三、waitpid() 函数(更强大)

函数原型

pid_t waitpid(pid_t pid, int *wstatus, int options);

参数

  1. pid
  • pid > 0 :等待PID 等于 pid 的子进程

  • pid = -1 :等待任意子进程(和 wait 一样)

  • pid = 0 :等待同组任意子进程

  • pid < -1 :等待组 ID 等于 |pid| 的任意子进程

  1. wstatus

输出型参数,存退出状态,同 wait()。

  1. options
  • 0 :阻塞等待

  • WNOHANG :非阻塞等待(有就收,没有立刻返回)

返回值

  • 0:返回退出的子进程 PID

  • =0:非阻塞模式下,子进程还没退出

  • -1:出错

四、两个宏(必背!)

用来从 wstatus 里取出子进程信息:

  1. WIFEXITED(status)
  • 子进程正常退出返回真
  1. WEXITSTATUS(status)
  • 取出子进程的退出码(0~255)

示例:

if(WIFEXITED(status)){

printf("退出码:%d\n", WEXITSTATUS(status));

}

五、核心区别

  1. wait 只能阻塞、等任意子进程

  2. waitpid 可以:

  • 指定等待某个子进程

  • 选择阻塞 / 非阻塞

  • 功能更强,是wait 的升级版

实例:

pid_t pid = fork();

if(pid == 0){

// 子进程

exit(123);

} else {

int status;

// 父进程等待

waitpid(pid, &status, 0);

// 或者 wait(&status);

}

如果父进程wait子进程,但是子进程就是没有退出,则父进程会阻塞再wait函数中。

相关推荐
PythonFun1 小时前
HAProxy端口转发入门:从“搬砖工”到“智能交通警察”
服务器·后端·网络安全
礼拜天没时间.1 小时前
企业级Docker镜像仓库Harbor部署实战
linux·运维·docker·云原生·容器·sre
晚风_END1 小时前
Linux|操作系统|小技巧---vim编辑的脚本自动添加shebang 和注释
linux·运维·vim
能源革命2 小时前
Ubuntu24.04修改ssh默认端口
linux·服务器·ssh
你们补药再卷啦2 小时前
运行python项目常用工具的安装和使用(ubuntu22)
linux·运维·服务器
白云偷星子2 小时前
RHCSA笔记4
运维
Hank Nie2 小时前
操作系统实践 0 | xv6入门与配置
linux·运维·服务器·系统架构
怀旧,2 小时前
【Linux系统编程】17. 进程间通信(下)
linux·运维·microsoft
czxyvX2 小时前
011-Linux进程控制
linux