一、进程退出方式
1、正常终止
主要两种类型:从 main
返回、调用 exit
和 _exit
具体讲解如下:
(1)从 main
返回
这个的使用就不用多说了吧,相信你们已经烂熟于心了
- 作用:
return
语句用于从函数中返回一个值,并终止该函数的执行。在main
函数中,return
语句用于终止整个程序的执行,并返回一个状态码给操作系统。 - 范围:
return
仅在函数内部有效,不能从全局作用域或其他地方使用。 - 清理工作:在
main
函数中使用return
时,会执行标准 I/O 缓冲区的刷新、关闭打开的文件描述符等清理工作,也会调用在atexit
注册的函数。
执行 return x
等同于执行 exit(x)
,因为调用 main
的运行时函数会将 main
的返回值当做 exit
的参数。
而 exit
函数如何使用,看下文解释:
(2)调用 exit
和 _exit
这两个系统调用函数可以立即终止整个程序的执行,但是他们两还是有不少区别的,这篇博客有更加详细的讲解:【Linux】 exit 和 _exit 的区别-CSDN博客
(3)return
和 exit
的异同点
作用范围:
return
:仅在函数内部有效,主要用于从函数中返回一个值。exit
和_exit
:可以在程序的任何地方调用,用于立即终止整个程序的执行。
清理工作:
return
和exit
:都会执行标准 I/O 缓冲区的刷新、关闭打开的文件描述符等清理工作,并调用在atexit
注册的函数。
2、异常终止
引入:ctrl + c
我们常常通过 ctrl + c
使当前正在执行的进程停下,其背后的原理是:
ctrl + c
:信号终止,被强行杀掉
当用户在终端中按下 Ctrl+C
时,会向当前的前台进程发送 SIGINT
(Interrupt)信号。这个信号通常用于中断或请求终止正在运行的程序。
正文如下
进程因为异常而中断退出的情况通常是指由于某些内部错误或外部事件导致进程无法继续执行,从而被迫终止。OS提前使用信号终止你的进程,下面是几种常见的异常情况及其原因:
(1) 访问非法内存地址(Segmentation Fault)
即为 野指针 问题
当进程试图访问不允许访问的内存区域时,会触发 SIGSEGV
信号,导致进程被中断并退出。
示例代码
#include <stdio.h>
#include <stdlib.h>int main() {int *ptr = NULL;*ptr = 42; // 尝试访问空指针指向的内存return 0;
}
(2) 除零错误(Division by Zero)
当进程执行除法运算时除数为零,会触发 SIGFPE
信号,导致进程被中断并退出。
示例代码
#include <stdio.h>
#include <stdlib.h>int main() {int result = 1 / 0; // 除零错误return 0;
}
(3)系统调用失败
当进程执行某些系统调用时发生错误,可能会导致进程异常退出。例如,打开文件失败、创建文件失败等情况。
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd = open("nonexistent_file.txt", O_RDONLY);if (fd == -1) {perror("open");return EXIT_FAILURE;}close(fd);return 0;
}
(4)文件权限问题
当进程试图访问没有权限访问的文件时,会触发 SIGBUS
或 SIGSEGV
信号,导致进程被中断并退出。
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd = open("/root/protected_file.txt", O_RDONLY);if (fd == -1) {perror("open");return EXIT_FAILURE;}close(fd);return 0;
}
(5) 进程被杀死
当进程被其他进程或管理员使用 kill
命令发送 SIGKILL
信号时,进程会被强制终止,而不会执行任何清理工作。
示例命令
kill -9 <PID>
总结
进程因为异常而中断退出的情况包括但不限于:
- 访问非法内存地址(野指针):尝试访问不允许访问的内存区域,触发
SIGSEGV
信号。 - 除零错误:执行除法运算时除数为零,触发
SIGFPE
信号。 - 系统调用失败:执行某些系统调用时发生错误,导致进程异常退出。
- 文件权限问题:试图访问没有权限访问的文件,触发
SIGBUS
或SIGSEGV
信号。 - 进程被杀死:被其他进程或管理员使用
SIGKILL
信号强制终止。
了解这些异常情况及其原因有助于更好地调试和优化程序,确保进程能够在各种环境下稳定运行。
二、进程退出码的获取
1、退出状态码的作用
前面讲解过,无论是通过 return
或 exit/_exit
正常退出,还是被信号终止或停止的异常退出,进程退出都会返回一个退出状态码给父进程,用于告知父进程:我这个退出的子进程是否正确执行任务,如果是异常退出,我又是因为什么原因退出的。
这个状态码包含了关于子进程退出原因的重要信息,父进程可以通过解析这个状态码来了解子进程的退出情况。
2、退出状态码是 16 位的整数
在 Unix 和 Linux 系统中,退出状态码通常是一个 16 位的整数,其中高 8 位和低 8 位分别包含不同的信息:
- 低 8 位:表示子进程的退出状态码(0-255)。对于正常退出,这是
return
或exit
传递的值。 - 高 8 位:包含其他信息,如终止信号编号等。
如下图的两行长方形,代表着两种进程退出时退出状态码的各个部分的作用:正常终止、被信号所杀
3、进程退出码的来源
当子进程终止时,操作系统会记录子进程的退出状态码,并将其保存在子进程的进程控制块(PCB)中。
父进程调用 wait
或 waitpid
时,操作系统会将子进程的退出状态码返回给父进程。
进程退出码并不是通过我们下面几条程序代码函数返回的,而是操作系统负责返回的,
我们下面几条程序要做的是设置与修改进程退出状态码,而不负责退出码返回。
(1)子进程正常退出:
当进程正常退出时,操作系统会设置退出状态码为 return
或 exit/_exit
传递的值。
main
函数return status
返回:默认返回值为0
(表示成功退出)exit(int status)
返回_exit(int status)
返回
(一个进程只要是通过 exit 或 _exit
函数直接退出的,就算做正常退出!)
(2)子进程异常退出:
当进程因接收到信号而终止时,操作系统会设置退出状态码的高 8 位为信号编号,低 8 位通常为 0。
4、如何获取进程退出码
而我们可以通过一些系统宏定义来获取进程的退出状态码,本篇博客中有这些宏定义的使用与使用示例:【Linux】如何通过系统宏定义,获取进程的退出码或退出信号-CSDN博客