当前位置: 首页 > news >正文

Linux 进程退出和进程控制

1. 进程退出

进程退出大致可以分为三种情况

代码运行完毕,结果正确
代码运行完毕,结果不正确
代码异常终止
像我们日常c语言的return 0就属于代码运行完毕 结果正确
如果返回的是其他值 那么可能就是代码运行完毕,结果不正确
我们先来看代码运行完毕的情况
我们先学习进程结束的几个函数

exit  _exit   和 return的区别

1.return 对比 exit和_exit的区别

return 在函数内  return后程序继续进行

但是exit和_exit在程序结束后就停止进行了

2.exit和_exit的区别

exit():是标准库函数(属于 C 标准库),声明在 <stdlib.h> 中。

_exit():是系统调用(直接与操作系统交互),声明在 <unistd.h> 中。

exit():终止进程前会执行一系列清理操作:

调用通过 atexit() 或 on_exit() 注册的清理函数(用户自定义的收尾逻辑)。

关闭所有已打开的标准 I/O 流(如 FILE* 指针),并刷新缓冲区(将缓冲区中的数据写入实际文件或设备)。

最终调用 _exit() 完成进程终止。

_exit():直接终止进程,不执行任何清理操作:

不会调用 atexit() 注册的函数。 不会刷新标准 I/O 缓冲区(缓冲区中的数据会丢失)。

直接释放进程占用的资源(如内存、文件描述符等)并返回内核。

进程异常终止情况

进程一般异常终止 不一定有返回值 比如我下面的这个代码 

由于对空指针进行简引用 所以程序会异常终止

这个时候没有返回值 于是进程会返回一个信号 这个信号可以通过kill -l

进行查看

从输出可知,可执行文件运行时出现 Segmentation fault(段错误),对应的信号是 SIGSEGV。在 kill -l 列出的信号列表中,SIGSEGV 的编号是 11。

2.打印返回值

我们的返回值可以用echo 打印  返回值表示?

但是和打印PSTH一样 要前面加一个$

为什么第二次echo打印的是0

因为echo $? 打印的是最近的一次进程的返回值

第二次echo $? 打印的是上一次echo $?的返回值

上一次echo $?是正常返回所以是0

3.进程等待

我们前面学习过了进程有一个状态是僵亡状态

但是我们没有解决怎么让父进程回收

让子进程从僵亡态(zombile)到死亡态(dead)

我们发现我们的将亡的子进程直接被父进程回收了

因此wait是可以将僵亡的进程直接回收的

1.wait函数

pid_t wait(int *status);

头文件:需要包含 <sys/wait.h> 和 <sys/types.h>

父进程调用 wait 后,会阻塞等待任意一个子进程终止,并回收该子进程的资源

如果父进程没有子进程,或所有子进程都已被回收,wait 会立即返回错误。

1.status

参数 status 作用:用于存储子进程的退出状态信息(如退出码、是否被信号终止等)。

特殊值:若传 NULL,表示父进程 “不关心子进程的退出状态”,仅需回收资源(如之前测试代码中的 wait(NULL))。

2. 返回值

成功:返回被回收的子进程的 PID(唯一标识哪个子进程被回收)。

失败:返回 -1,并设置 errno(如 errno=ECHILD 表示没有子进程可回收)。

当 status 不为 NULL 时,它存储的是子进程的 “原始退出状态”(一个整数,需通过系统提供的宏解析)。常用宏如下:

// 父进程代码片段
int status;
pid_t ret = wait(&status);  // 等待子进程并获取状态
if (ret != -1) {if (WIFEXITED(status)) {// 子进程正常退出,打印退出码printf("子进程%d正常退出,退出码:%d\n", ret, WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {// 子进程被信号终止,打印信号值printf("子进程%d被信号%d终止\n", ret, WTERMSIG(status));}
}

wait 是 waitpid 的简化版,等价于 waitpid(-1, status, 0)(-1 表示等待任意子进程,0 表示阻塞模式)。

阻塞性: 父进程调用 wait 后会 “卡住”(阻塞),直到有子进程终止才会继续执行。如果已有子进程终止但未被回收(即已成为僵尸进程),wait 会立即返回并回收它。

回收范围: wait 只能回收当前进程的子进程(不能回收孙进程或其他无关进程)。如果有多个子进程,wait 会按 “子进程终止的先后顺序” 回收其中一个(不确定具体是哪一个,除非配合信号机制)。

2.pid_wait函数

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);

1.pid

pid:指定等待的子进程 pid 的值决定了 waitpid 等待哪些子进程,具体规则如下:

pid > 0:等待进程 ID 为 pid 的特定子进程。

pid = 0:等待与调用进程(父进程)同进程组的任意子进程(即组内所有子进程)。

pid = -1:等待任意子进程(与 wait 函数功能一致)。

pid < -1:等待进程组 ID 为 |pid|(pid 的绝对值)的任意子进程(即指定进程组内的子进程)。

2.status

status:存储子进程的状态信息 wstatus 是一个指向整数的指针,

用于接收子进程的状态信息(如退出原因、信号等)。

若不需要状态信息,可传入 NULL。

通过 <sys/wait.h> 提供的宏可以解析 wstatus 的值,常用宏如下:

3. options

控制 waitpid 的行为 options 是位掩码,用于设置等待的模式,常用选项如下(可通过 | 组合): WNOHANG:非阻塞模式。若指定的子进程未发生状态变化,waitpid 立即返回 0,而非阻塞等待。

WUNTRACED:除了等待子进程终止,还返回被信号暂停的子进程状态。

WCONTINUED:返回从暂停状态恢复(收到 SIGCONT)的子进程状态。

4.返回值

成功:返回状态变化的子进程的 PID(若子进程终止,会清理其僵尸进程状态)。

若设置 WNOHANG 且无符合条件的子进程状态变化:返回 0。

失败:返回 -1(如无符合条件的子进程、被信号中断等),并设置 errno 表示错误原因(如 ECHILD 表示无待等待的子进程)。

5.与 wait 函数的区别

wait 函数可视为 waitpid 的简化版,等价于 waitpid(-1, wstatus, 0),

即: wait 只能等待任意子进程,而 waitpid 可通过 pid 指定特定子进程或进程组。

wait 是阻塞的,而 waitpid 可通过 WNOHANG 实现非阻塞等待。

wait 不支持获取子进程的暂停 / 继续状态,而 waitpid 可通过 WUNTRACED/WCONTINUED 实现。

4.status结构

我们举个例子来试试

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid == -1) {perror("fork 失败");exit(EXIT_FAILURE);}if (pid == 0) {printf("子进程 (PID: %d) 即将以退出码 1 结束\n", getpid());exit(1);} else {int status;pid_t waited_pid = wait(&status);if (waited_pid == -1) {perror("wait 失败");exit(EXIT_FAILURE);}printf("\n父进程捕获到子进程 (PID: %d) 的结束状态\n", waited_pid);printf("wait 返回的原始 status 值: %d\n", status);if (WIFEXITED(status)) {int exit_code = WEXITSTATUS(status);printf("子进程正常退出,退出码: %d\n", exit_code);printf("验证:退出码 << 8 = %d(与原始 status 一致)\n", exit_code << 8);} else {printf("子进程未正常退出(可能被信号终止)\n");}}return 0;
}

为什么status的值256呢???

在类 Unix 系统中,进程的终止状态通过一个整数(通常是 8 位或 16 位结构)来表示,这张图将其拆分为两种场景:

正常终止:进程通过 exit() 等正常逻辑结束。

此时 ** 高 8 位(位 8-15)** 存储 “退出状态码”,** 低 8 位(位 0-7)** 为 0(或对应正常退出的结构)。

被信号所杀:进程因收到某个系统信号(如 SIGSEGV、SIGKILL 等)而终止。

此时高 8 位未用,低 8 位中包含两部分: core dump 标志位:标识进程终止时是否生成了核心转储文件(用于调试);

终止信号编号:标识导致进程终止的具体信号。

我们直接看代码1和代码2

//代码1
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) { // 子进程exit(123); // 正常退出,状态码设为123} else { // 父进程int status;wait(&status); // 等待子进程if (WIFEXITED(status)) { // 判断正常退出printf("子进程正常退出,状态码:%d\n", WEXITSTATUS(status));}}return 0;
}

代码1 正常退出 因为exit(123)所以高八位存123

所以WEXITSTATUS提取到的是123 所以打印的是123

//代码2
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>int main() {pid_t pid = fork();if (pid == 0) { // 子进程sleep(2); // 让子进程先休眠,方便父进程发信号} else { // 父进程sleep(1); // 等1秒后给子进程发终止信号kill(pid, SIGKILL); // 发SIGKILL信号杀死子进程int status;wait(&status); // 等待子进程if (WIFSIGNALED(status)) { // 判断信号终止printf("子进程被信号终止,信号编号:%d\n", WTERMSIG(status));// SIGKILL的编号是9,可验证}}return 0;
}

代码2 由于是被信号所杀 所以低七位存信号编号 高位无意义 由于这个地方被SIGKILL杀死 其编号是9 所以  WTERMSIG提取到的是 打印出来的是9

http://www.dtcms.com/a/546116.html

相关文章:

  • 计算机网络自顶向下方法14——应用层 DNS详解 工作机理
  • [GXDE软件包安装器]在龙芯deepin上一键安装旧世界包
  • dw设计模板系统优化的方法知识点
  • 【AI】人工智能之PINN和贝叶斯
  • 毕业设计答辩网站开发原理江苏省网站建设
  • 网站备案照湘潭做网站选择磐石网络
  • 如何做网站的二级页面500网站建设
  • Python 高效实现 Excel 与 TXT 文本文件之间的数据转换
  • mysql重置密码
  • Spring MVC 数据校验
  • Rust 中所有权与零成本抽象的深度关联:原理与实践
  • 被通知公司网站域名到期搭建企业资料网站
  • 仓颉语言宏系统的设计与应用:从元编程到领域特定语言
  • 【GUI】本地电脑弹出远程服务器的软件GUI界面
  • 仓颉技术:Union类型的定义与应用
  • 闲置电脑做网站服务器重庆互联网公司排行榜
  • 连续值和缺失值详解
  • 仓颉FFI外部函数接口:跨语言互操作的工程实践
  • 串口、RS-232与RS-485应用全解析
  • 推广公司网站premium WordPress
  • 成都建站seo奉贤集团公司网站建设
  • 网站的空间和域名iis内网站设置允许脚本执行
  • 商旅平台定义、选型逻辑与2025主流商旅平台汇总
  • 0144. 二叉树的前序遍历
  • 做网站的钱叫什么科目建设工程自学网站
  • 自动驾驶汽车与利益相关者互动的功能安全与网络安全分析方法
  • 如何将本地项目上传至github
  • 整合STPA、ISO 26262与SOTIF的自动驾驶安全需求推导与验证
  • 广东网站备案系统北京网页设计机构
  • Linux系统启动光盘/U盘制作