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函数中。

相关推荐
同聘云几秒前
阿里云国际站服务器DNS服务器设置成什么?服务器dns怎么填写?
服务器·阿里云·云计算·云小强
小此方几秒前
Re:Linux系统篇(六)权限篇 · 一:用户切换与进程嵌套&&sudo提权与sudoers设置精讲
linux·运维·服务器
原来是猿2 分钟前
Linux线程同步与互斥(五):线程池的全面实现
linux·服务器·开发语言
王九思3 分钟前
Ansible 自动化运维基础—模板
运维·自动化·ansible
嵌入式×边缘AI:打怪升级日志4 分钟前
从零开始学习 Linux SPI 驱动开发(基于 IMX6ULL + TLC5615 DAC)
linux·驱动开发·学习
feng_you_ying_li4 分钟前
linux之进程控制
linux
Mr_pyx7 分钟前
CompletableFuture 使用全攻略:从异步编程到异常处理
linux·前端·python
开开心心_Every7 分钟前
跨平台高速下载工具,支持浏览器功能强大
运维·服务器·随机森林·pdf·电脑·逻辑回归·excel
拾贰_C23 分钟前
【OpenAI | Ubuntu | bigmodel】 openai规范配置bigmodel(zhipu)大模型api
linux·运维·ubuntu
计算机安禾26 分钟前
【Linux从入门到精通】第22篇:Shell变量与数据类型——数字与字符串处理
linux·运维·chrome