一、进程终止的核心场景

正常终止(代码完整运行完毕)

  • 成功:进程执行到main函数结束或调用exit(),返回退出码 0(约定为执行成功)。
  • 失败:代码执行完毕但结果异常,返回非零退出码(如1表示内存错误,2表示文件错误)。

异常终止

  • 触发条件
    • 运行时发生致命错误(如野指针、除零操作)。
    • 收到操作系统或其他进程发送的强制终止信号(如 kill 命令)。
  • 关键特征
    • 未执行到 return 或 exit,无显式退出码。
    • 终止由操作系统通过信号机制强制完成(后续详细讲解,此处先理解为 “错误通知”)。

二、正常终止:退出码的本质与用途

(一)main函数返回值的含义

退出码:进程向父进程(或 Shell)传递的执行状态标识。

  • return 0; → 成功(如命令行中echo $?输出0)。
  • return non-zero; → 失败,非零值可自定义为具体错误类型(如1= 内存申请失败)。

接收者

  • Shell:通过echo $?获取上一个进程的退出码。
  • 父进程:通过特定接口获取(后续补充)。

(二)echo $?命令与退出码查看

作用:查看命令行中最近一个正常终止进程的退出码。

$ ./program  # 假设程序返回退出码3
$ echo $?    # 输出3(非零表示失败)
$ ls -l       # 正常执行,退出码为0
$ echo $?    # 输出0

(三)代码示例:退出码的设置与应用

1.基础示例:返回成功 / 失败码

#include <stdio.h>
int main() {int result = 1; // 1表示成功,0表示失败return result ? 0 : 1; // 成功返回0,失败返回1
}

2.系统错误码示例(文件打开失败)

#include <stdio.h>
#include <errno.h> // 包含系统错误码定义int main() {FILE *fp = fopen("nonexist.txt", "r");if (!fp) {return errno; // 返回系统错误码(如2表示"文件不存在")}fclose(fp);return 0;
}
  • 运行后echo $?输出2,对应错误描述为 "No such file or directory"。

3.自定义错误码体系

#define ERR_MEMORY 1 // 内存申请失败
#define ERR_FILE 2   // 文件操作失败int main() {char *p = (char*)malloc(1024);if (!p) return ERR_MEMORY; // 内存不足时返回1free(p);return 0;
}

三、错误码的解析与转换

(一)strerror:错误码转文本描述

  • 作用:将整数错误码转换为可读字符串(需包含string.h)。
  • 示例:打印系统错误码描述
    #include <stdio.h>
    #include <string.h>int main() {for (int i = 0; i < 200; i++) {printf("%d: %s\n", i, strerror(i)); // 输出如"2: No such file or directory"}return 0;
    }
    • 运行结果:
      0:Success
      1:Operation not permitted
      2:No such file or directory
      3:No such process
      4:Interrupted system call
      5:Input/output error
      6:No such device or address
      7:Argument list too long
      8:Exec format error
      9:Bad file descriptor
      10:No child processes
      11:Resource temporarily unavailable
      12:Cannot allocate memory
      13:Permission denied
      14:Bad address
      15:Block device required
      16:Device or resource busy
      17:File exists
      18:Invalid cross-device link
      19:No such device
      20:Not a directory
      21:Is a directory
      22:Invalid argument
      23:Too many open files in system
      24:Too many open files
      25:Inappropriate ioctl for device
      26:Text file busy
      27:File too large
      28:No space left on device
      29:Illegal seek
      30:Read-only file system
      31:Too many links
      32:Broken pipe
      33:Numerical argument out of domain
      34:Numerical result out of range
      35:Resource deadlock avoided
      36:File name too long
      37:No locks available
      38:Function not implemented
      39:Directory not empty
      40:Too many levels of symbolic links
      41:Unknown error 41
      42:No message of desired type
      43:Identifier removed
      44:Channel number out of range
      45:Level 2 not synchronized
      46:Level 3 halted
      47:Level 3 reset
      48:Link number out of range
      49:Protocol driver not attached
      50:No CSI structure available
      51:Level 2 halted
      52:Invalid exchange
      53:Invalid request descriptor
      54:Exchange full
      55:No anode
      56:Invalid request code
      57:Invalid slot
      58:Unknown error 58
      59:Bad font file format
      60:Device not a stream
      61:No data available
      62:Timer expired
      63:Out of streams resources
      64:Machine is not on the network
      65:Package not installed
      66:Object is remote
      67:Link has been severed
      68:Advertise error
      69:Srmount error
      70:Communication error on send
      71:Protocol error
      72:Multihop attempted
      73:RFS specific error
      74:Bad message
      75:Value too large for defined data type
      76:Name not unique on network
      77:File descriptor in bad state
      78:Remote address changed
      79:Can not access a needed shared library
      80:Accessing a corrupted shared library
      81:.lib section in a.out corrupted
      82:Attempting to link in too many shared libraries
      83:Cannot exec a shared library directly
      84:Invalid or incomplete multibyte or wide character
      85:Interrupted system call should be restarted
      86:Streams pipe error
      87:Too many users
      88:Socket operation on non-socket
      89:Destination address required
      90:Message too long
      91:Protocol wrong type for socket
      92:Protocol not available
      93:Protocol not supported
      94:Socket type not supported
      95:Operation not supported
      96:Protocol family not supported
      97:Address family not supported by protocol
      98:Address already in use
      99:Cannot assign requested address
      100:Network is down
      101:Network is unreachable
      102:Network dropped connection on reset
      103:Software caused connection abort
      104:Connection reset by peer
      105:No buffer space available
      106:Transport endpoint is already connected
      107:Transport endpoint is not connected
      108:Cannot send after transport endpoint shutdown
      109:Too many references: cannot splice
      110:Connection timed out
      111:Connection refused
      112:Host is down
      113:No route to host
      114:Operation already in progress
      115:Operation now in progress
      116:Stale file handle
      117:Structure needs cleaning
      118:Not a XENIX named type file
      119:No XENIX semaphores available
      120:Is a named type file
      121:Remote I/O error
      122:Disk quota exceeded
      123:No medium found
      124:Wrong medium type
      125:Operation canceled
      126:Required key not available
      127:Key has expired
      128:Key has been revoked
      129:Key was rejected by service
      130:Owner died
      131:State not recoverable
      132:Operation not possible due to RF-kill
      133:Memory page has hardware error
      134:Unknown error 134
      135:Unknown error 135
      136:Unknown error 136
      137:Unknown error 137
      138:Unknown error 138
      139:Unknown error 139
      140:Unknown error 140
      141:Unknown error 141
      142:Unknown error 142
      143:Unknown error 143
      144:Unknown error 144
      145:Unknown error 145
      146:Unknown error 146
      147:Unknown error 147
      148:Unknown error 148
      149:Unknown error 149
      150:Unknown error 150
      151:Unknown error 151
      152:Unknown error 152
      153:Unknown error 153
      154:Unknown error 154
      155:Unknown error 155
      156:Unknown error 156
      157:Unknown error 157
      158:Unknown error 158
      159:Unknown error 159
      160:Unknown error 160
      161:Unknown error 161
      162:Unknown error 162
      163:Unknown error 163
      164:Unknown error 164
      165:Unknown error 165
      166:Unknown error 166
      167:Unknown error 167
      168:Unknown error 168
      169:Unknown error 169
      170:Unknown error 170
      171:Unknown error 171
      172:Unknown error 172
      173:Unknown error 173
      174:Unknown error 174
      175:Unknown error 175
      176:Unknown error 176
      177:Unknown error 177
      178:Unknown error 178
      179:Unknown error 179
      180:Unknown error 180
      181:Unknown error 181
      182:Unknown error 182
      183:Unknown error 183
      184:Unknown error 184
      185:Unknown error 185
      186:Unknown error 186
      187:Unknown error 187
      188:Unknown error 188
      189:Unknown error 189
      190:Unknown error 190
      191:Unknown error 191
      192:Unknown error 192
      193:Unknown error 193
      194:Unknown error 194
      195:Unknown error 195
      196:Unknown error 196
      197:Unknown error 197
      198:Unknown error 198
      199:Unknown error 199
      

(二)errno:最近一次错误码

  • 作用:C 标准库全局变量,存储最近一次函数调用失败的错误码(需包含errno.h)。
  • 示例:结合errno定位问题
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>int main() {char *p = malloc(4 * 1024 * 1024 * 1024); // 申请大内存可能失败if (!p) {printf("malloc error: errno=%d (%s)\n", errno, strerror(errno));return errno; // 返回系统错误码(如12表示"Cannot allocate memory")}free(p);return 0;
    }

 四、异常终止的本质:硬件错误与信号

当程序出现非法操作时,CPU 会检测到硬件异常(如访问无效内存),操作系统将其转化为信号发送给进程,强制终止程序。

常见异常信号与对应错误

信号名称编号错误场景举例终端提示信息
SIGSEGV11野指针(访问空指针或无效内存)Segmentation fault
SIGFPE8除零操作、浮点运算错误Floating point exception
SIGKILL9强制终止(如 kill -9 命令)无提示,直接终止

代码示例 1:野指针导致异常终止

#include <stdio.h>
int main() {char* p = NULL; // 空指针,无合法内存映射*p = 100;       // 尝试向空指针写入数据,触发SIGSEGVreturn 0;       // 此行代码不会执行
}

运行结果

Segmentation fault  # 操作系统发送SIGSEGV信号终止进程

五、退出码的有效性:何时需要关注?

1. 退出码有意义的前提

  • 仅当进程正常终止(未收到任何异常信号)时,main 的 return 或 exit 设置的退出码才有意义。

2. 异常场景下退出码不可靠

  • 若进程因信号终止(如野指针),未执行到 return,无退出码;
  • 即使执行了 return,若系统资源不足导致退出失败,退出码也可能失效。
  • 结论
    • 异常终止时,优先排查信号原因(如为什么收到 SIGSEGV),而非关注退出码;
    • 正常终止时,再通过退出码判断逻辑结果(0 成功,非 0 失败)。

六、kill命令模拟异常终止(实践验证)

代码示例 2:运行中进程接收信号测试

#include <stdio.h>
#include <unistd.h> // getpid() 获取进程ID
int main() {while (1) {printf("运行中,PID: %d\n", getpid()); // 打印进程IDsleep(1); // 每秒输出一次,方便观察}return 0; // 死循环,无法执行到此处
}

操作步骤

1.编译并运行程序

2.另开终端查看进程 PID(假设输出为 29084

ps ajx | head -1 && ps ajx | grep test_signal

3.模拟异常终止:

  • 发送 SIGFPE(除零错误信号):
    kill -8 12345  # 终端显示:Floating point exception
  • 发送 SIGSEGV(段错误信号):
    kill -11 12345 # 终端显示:Segmentation fault
  • 发送 SIGKILL(强制终止信号):
    kill -9 12345  # 进程直接终止,无错误提示

验证结论

  • 进程异常终止的本质是收到特定信号,信号编号对应不同错误类型。
  • 通过 kill -信号编号 PID 可主动模拟各类异常场景。

七、进程状态判断的核心逻辑

第一步:是否异常终止?

  • 判断依据:是否收到信号(如 SIGSEGV/SIGFPE)。
  • 处理方式
    • 是:优先排查代码中的硬件级错误(如野指针、除零)。
    • 否:进入下一步判断。

第二步:退出码是否成功?

  • 判断依据:正常终止时的退出码(return 或 exit 的值)。
  • 处理方式
    • 退出码=0:程序逻辑执行成功。
    • 退出码≠0:程序逻辑执行失败(如参数错误、文件读取失败等)。

八、进程终止:return、exit 与_exit 的区别

1.进程终止的三种核心方式

return(C关键字)

  • 作用范围
    • 在普通函数中:仅表示当前函数返回,程序继续执行调用处的后续代码(不终止进程)。
    • main函数中:等价于exit(返回值),会终止进程并设置退出码(如return 0表示成功)。
  • 核心特点
    • 依赖函数调用关系,不能在非main函数中直接终止进程。
    • main函数中使用时,会隐式调用exit完成资源清理(如刷新缓冲区)。
  • 代码示例:return 在不同场景的差异
    // 场景1:return在普通函数中仅返回
    void show() {printf("进入show函数\n");return; // 函数返回,不终止进程printf("show函数结束(不会执行)\n"); // 此行代码不会执行
    }int main() {show(); // 调用show函数后返回printf("回到main函数继续执行\n"); // 会执行return 12; // main中return等价于exit(12),终止进程
    }

    运行结果

    进入show函数
    回到main函数继续执行

    echo $?输出12(退出码为 12)。

exit(C 标准库函数)

  • 作用
    • 在任意函数中调用:立即终止进程,并执行清理操作(如刷新缓冲区、关闭文件流)。
    • 可显式设置退出码(范围:0~255,0 表示成功,非 0 表示错误)。
  • 与 main 中 return 的等价性
    int main() {exit(12); // 完全等价于 return 12;
    }

    验证:编译运行后,echo $?输出12

_exit(系统调用)

  • 作用
    • 直接调用操作系统内核接口终止进程,不执行任何用户空间清理操作(如不刷新缓冲区)。
    • 属于底层系统调用,比exit更 “轻量”,但可能导致数据丢失(若有未刷新的缓冲区)。
  • 与 exit 的关系
    exit(n) = 先执行清理操作(刷新缓冲区等) → 再调用 _exit(n);

2.exit vs _exit:缓冲区刷新的关键区别

缓冲区的刷新规则

  • printf的缓冲区位于用户空间
    • 若输出内容包含\n,或调用fflush,或进程通过exit终止,缓冲区会被刷新,数据输出到终端。
    • 若进程通过_exit终止,缓冲区不会被刷新,数据可能丢失。

代码示例 1:带\n的输出(缓冲区自动刷新)

int main() {printf("带\n的输出:hello world!\n"); // \n触发缓冲区刷新_exit(11); // 即使调用_exit,数据已刷新,会显示return 0;
}

运行结果:

代码示例 2:不带\n的输出(依赖终止方式刷新)

int main() {printf("不带\n的输出:hello world!"); // 数据暂存用户空间缓冲区// exit(11);    // 调用exit会刷新缓冲区,数据会显示_exit(11);     // 调用_exit不刷新缓冲区,数据不显示return 0;
}

运行结果

  • exit(11):终端显示输出,echo $?输出11
  • _exit(11):终端无输出,echo $?输出11

清理操作的差异

特性exit(库函数)_exit(系统调用)
缓冲区处理刷新用户空间缓冲区(如printf的数据)不刷新缓冲区,直接终止进程
资源清理关闭文件流、执行注册的清理函数仅回收内核资源,不处理用户空间逻辑
调用层级上层库函数(调用_exit前先做清理)底层系统调用(直接终止进程)
适用场景日常开发(确保数据正确输出)底层场景(如需要快速终止的程序)

3.return vs exit:作用域与灵活性对比 

代码示例:return 在非 main 函数中

void show() {printf("进入show函数\n");return 13;printf("show函数结束(不会执行)\n"); // 此行代码不会执行
}int main() {show(); // 调用show后直接终止,不会回到mainprintf("回到main\n");return 12;
}

运行结果:

echo $?输出13(exit 设置的退出码)。

代码示例:exit 在非 main 函数中终止

void show() {printf("进入show函数\n");exit(13); // 无论在哪层函数,exit都会终止进程printf("show函数结束(不会执行)\n"); // 此行代码不会执行
}int main() {show(); // 调用show后直接终止,不会回到mainprintf("回到main\n");return 12;
}

运行结果:

echo $?输出13(exit 设置的退出码)。

特性return(仅在 main 中终止进程)exit(任意位置终止进程)
终止范围仅在main函数中终止进程在任意函数中调用均终止进程
代码位置必须位于main函数末尾可位于程序任意位置(如循环、条件语句中)
缓冲区处理等价于exit(隐式调用exit显式刷新缓冲区
典型场景main函数逻辑正常结束时返回结果需在非main函数中强制终止进程

4.核心结论

  • returnmain中等价于exit,但在普通函数中仅返回;
  • exit是 “安全终止”,会刷新缓冲区,确保数据输出;
  • _exit是 “暴力终止”,适合底层场景,但可能导致数据丢失。
  • 优先使用exitmain中的return,确保程序行为可预期。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/89896.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/89896.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/89896.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Milvus docker-compose 部署

文章目录 前言Milvus docker-compose 部署1. 下载2. 修改配置3. 启动4. 测试 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的…

EveryThing搜索具体路径下文件中的内容

1.打开EveryThing 2.点击搜索&#xff0c;选择高级搜索 3.选择需要搜索的文件的路径以及文件中需要包含的内容 4.之后就可以搜索到对应的目标文件

【算法】宽度优先遍历BFS

二叉树的宽搜 429、N叉树的层序遍历 题解 BFS核心思想 二叉树的宽搜一般都是借助队列来实现的&#xff0c;实现的原理为首先将根节点进行放入队列中&#xff0c;然后将根节点进行弹出的时候&#xff0c;将这个节点的孩子节点进行放入队列中&#xff0c;然后继续弹出队头的元…

【STM32】通用定时器基本原理

STM32 通用定时器基本原理&#xff08;基于 STM32F1&#xff09;参考资料&#xff1a;STM32F1xx官方资料&#xff1a;《STM32中文参考手册V10》-第14章通用定时器STM32 定时器分类 STM32F103 系列共有三类定时器&#xff1a;&#x1f50e; 通用定时器&#xff08;TIM2~TIM5&…

【Go语言-Day 14】深入解析 map:创建、增删改查与“键是否存在”的奥秘

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…

Vue脚手架搭建项目+基础知识

1. 使用脚手架创建项目1.1 准备工作winR&#xff0c;在弹出的数据框中输入cmd&#xff0c;数据命令查看node以及npm版本 下载vue cli1.2 创建项目1.2.1 创建一个英文目录文件夹&#xff0c;cmd打开命令命令提示符1.2.2 vue ui命令打开控制台1.2.3 创建项目创建成功1.3 项目结构…

微信小程序下单页—地址列表页—新增地址页 页面交互

新增地址流程&#xff1a; 下单页 → 地址列表页 (1次跳转)地址列表页 → 新增地址页 (1次跳转)保存地址 → 返回地址列表页 (1次返回&#xff0c;自动刷新列表) 选择地址流程&#xff1a; 地址列表页 → 选中地址 → 返回下单页 (1次返回) 更换地址&#xff1a; 下单页 → 地址…

JVM与JMM

为了更清晰地对比JVM和JMM&#xff0c;我们可以采用表格形式&#xff0c;从定义、功能、结构、与多线程关系等方面进行详细比较&#xff1a; 对比项JVM&#xff08;Java Virtual Machine&#xff09;JMM&#xff08;Java Memory Model&#xff09;定义一种虚构的计算机&#x…

【Docker基础】Docker数据卷管理:docker volume rm及其参数详解

目录 1 引言&#xff1a;Docker Volume 的生命周期管理 2 docker volume rm命令基础 2.1 命令作用 2.2 命令语法 3 参数深度解析 3.1 基础参数表 3.2 高级参数详解 3.2.1 --force&#xff08;-f&#xff09; 4 Volume删除前置条件 4.1 可删除状态判断 4.2 常见报错处…

嵌入式系统内核镜像相关(十)

文章目录 前言一、点亮多个led灯的基础实验以及其中的问题1.1 基础流程1.1.1 alinx教程的问题1.1.1.1 驱动程序中的亮/灭逻辑修改&#xff01;1.1.1.1.1 逻辑错误的修改1.1.1.1.2 多灯亮/灭 1.1.1.2 驱动程序中引脚的问题以及与裸机开发的区别&#xff08;重要&#xff09;1.1.…

Word和Excel批量转PDF新方法,操作简单

PDF是一种跨平台的文档格式&#xff0c;无论在任何设备上查看&#xff0c;其排版、字体和图像都不会发生变化。这确保了文档的一致性&#xff0c;避免了由于不同软件版本或操作系统引起的显示问题。这款小巧的工具大小不到2MB&#xff0c;使用起来异常简单。只需要把需要转换的…

AI搜索 MCP最佳实践

背景 那些 LLM 不知道的事 尝试直接询问LLM“今天天气如何”时&#xff0c;会发现LLM无法回答——它既不知道“今天”是哪天&#xff0c;也无法获取地理位置信息。这揭示了LLM的局限&#xff1a;缺乏与外部工具和实时数据的交互能力。 为解决这一问题&#xff0c;MCP&#x…

JVM 简介与作用

&#x1f680; JVM 简介与作用 &#x1f4da; 深入理解 Java 虚拟机的核心概念与重要作用 &#x1f4d6; 目录 &#x1f914; 什么是 Java 虚拟机&#xff08;JVM&#xff09;&#x1f310; JVM 在 Java 生态中的核心地位&#x1f500; JVM 跨平台原理剖析&#x1f4dd; 总结 …

✨ OpenAudio S1:影视级文本转语音与语音克隆Mac整合包

✨ OpenAudio S1&#xff1a;影视级文本转语音与语音克隆Mac整合包 &#x1f680; OpenAudio S1 简介 OpenAudio S1 是由 Fish Audio 开发的 Fish Speech 系列的最新一代人工智能语音生成模型。该模型旨在大幅提升 AI 语音生成的技术水平&#xff0c;为用户提供更加自然、富有表…

spring加载外部properties文件属性时,读取到userName变量值和properties文件的值不一致

问题 使用spring DI注入外部properties文件属性时&#xff0c;读取到userName变量值和properties文件的值不一致。 bean属性注入&#xff1a; <!--加载配置文件--> <context:property-placeholder location"classpath:*.properties"/><bean id"…

黑马点评系列问题之基础篇p7 06初识redis无法在虚拟机查到图形化界面存进去的键

问题描述 在RESP中输入了一些键(name,age等这些) 但是在图形化界面里面输入的&#xff0c;在非图形化界面就找不到&#xff0c;在非图形化界面里输入的&#xff0c;在图形化界面里就可以查到。 原因分析及解决 经过多次实验&#xff0c;发现是因为在添加键名的时候&#xff0…

在VMware虚拟机中安装Windows 98时,Explorer提示“该程序执行了非法操作,即将关闭”的解决办法

在使用iso文件&#xff08;MD5: 0E496B5DCC519F550AAF0BCFBB4A11EA&#xff09;安装Windows98时&#xff0c;遇到此提示。 虽然原因未知&#xff0c;也无需深入探究&#xff0c;但是根据网友在 https://www.bilibili.com/opus/435866522585702782 中给出的相似经验&#xff…

在浏览器中使用SQLite(官方sqlite3.wasm)

有人可能会问&#xff1a;既然浏览器里又内置得IndexedDB&#xff0c;而且在IndexedDB里存数据&#xff0c;关了浏览器数据也不会丢&#xff0c;为什么还要在浏览器里用SQLite? 实际上&#xff0c;当 IndexedDB 内的数据量增多&#xff0c;数据和数据之间的关系变得复杂&…

数据结构(Java)--位运算

前言 本文为本小白学习数据结构的笔记&#xff0c;将以算法题为导向&#xff0c;向大家更清晰的介绍数据结构相关知识&#xff08;算法题都出自B站马士兵教育——左老师的课程&#xff0c;讲的很好&#xff0c;对于想入门刷题的人很有帮助&#xff09; 为什么要使用为位运算 位…

秋招Day14 - Redis - 应用

Redis如何实现异步消息队列&#xff1f; List配合LPUSH和RPOP。 另外就是用 Redis 的 Pub/Sub 来实现简单的消息广播和订阅。 但是这两种方式都是不可靠的&#xff0c;因为没有 ACK 机制所以不能保证订阅者一定能收到消息&#xff0c;也不支持消息持久化。 Redis如何实现延时…