linux 进程退出码,Linux 进程退出码

Linux 进程退出码

2018-11-04 Sunday

Linux 下进程的退出包括了正常退出和异常退出,正常退出包括了 A) main() 函数中通过 return 返回;B) 调用 exit() 或者 _exit() 退出。异常退出包括了 A) abort() 函数;B) 收到了信号退出。

不管是哪种退出方式,系统最终都会执行内核中的同一代码,并将进程的退出方式以返回码的方式保存下来。

简介

当进程正常或异常终止时,内核就向其父进程发送 SIGCHLD 信号,对于 wait() 以及 waitpid() 进程可能会出现如下场景:

如果其所有子进程都在运行则阻塞;

如果某个子进程已经停止,则获取该子进程的退出状态并立即返回;

如果没有任何子进程,则立即出错返回。

如果进程由于接收到 SIGCHLD 信号而调用 wait,则一般会立即返回;但是,如果在任意时刻调用 wait 则进程可能会阻塞。

等待子进程退出

父进程可以通过 wait() 或者 waitpid() 获取子进程的状态码,详细可以通过 man 3 wait 查看,其声明如下。

如果下面参数中的 status 不是 NULL,那么会把子进程退出时的状态返回,该返回值保存了是否为正常退出、正常结束的返回值、被那个信号终止等。

#include

pid_t wait(int *status);

pit_t waitpid(pid_t pid, int *status, int options);

当要等待一特定进程退出时,可调用 waitpid() 函数,其中第一个入参 pid 的入参含义如下:

pid=-1 等待任一个子进程,与 wait 等效。

pid>0 等待其进程 ID 与 pid 相等的子进程。

pid==0 等待其进程组 ID 等于调用进程组 ID 的任一个子进程。

pid

waitpid 返回终止子进程的进程 ID,并将该子进程的终止状态保存在 status 中,其中 waitpid() 第三个入参指定了一些行为,如下是常见的参数列表:

WNOHANG 没有任何已经结束的子进程则立刻返回,不等待。

WUNTRACED 子进程进入暂停执行情况则马上返回, 不会关心子进程的推出状态。

退出码

子进程结束后,其最终的状态信息保存在 status ,在 sys/wait.h 中有对相关宏定义的实现,其中退出码有效为 16Bits 主要由三部分组成,包括了:

Bits 8~15 通过 exit() 接口退出进程,也就是意味着错误码最大为 255 ,如果是 256 那么实际上是 0 ;

Bit 7 用来标示是否有生成 core 文件。

Bits 0~6 对应了接受到的信号,注意这里还通过 127 0x7f 定义了 STOP 状态。

头文件中提供的宏定义包括了:

WIFEXITED 判断是否通过 exit() return 正常退出,然后通过 WEXITSTATUS 获取具体的退出码。

WIFSIGNALED 判断是否是因为接受到了信号而停止,包括 core 的方式实际上也是通过信号完成,此时可通过 WTERMSIG 读取信号,WCOREDUMP 是否生成 coredump 文件。

WIFSTOPPED 判断子进程是否处于暂停执行状态,一般只有使用 WUNTRACED 参数时会返回该值。

其中示例代码如下。

#include

#include

#include

#include

#include

#include

#include

#define log_info(...) do { printf("info : " __VA_ARGS__); putchar('\n'); } while(0);

#define log_error(...) do { printf("error: " __VA_ARGS__); putchar('\n'); } while(0);

#define REPO_PATH "/tmp/examples/linux/process/exitcode"

int main(void)

{

int status;

pid_t pid;

char *argv[] = {(char *)"/bin/bash", (char *)"-c", (char *)"exit 1", NULL};

pid = fork();

if (pid < 0) {

log_error("fork failed, %s.", strerror(errno));

exit(EXIT_FAILURE); /* 1 */

} else if (pid == 0) { /* child */

//ptrace(PTRACE_TRACEME, 0, NULL, NULL);

if (execvp(argv[0], argv) < 0) {

log_error("execl failed, %s.", strerror(errno));

return 0;

}

}

if (waitpid(pid, &status, WUNTRACED) < 0) {

log_error("waitpid error, %s.", strerror(errno));

return 0;

}

log_info("process #%d exit with %d.", pid, status);

if (WIFEXITED(status)) {

log_info("normal termination, exit status = %d.", WEXITSTATUS(status));

} else if (WIFSIGNALED(status)) {

log_info("abnormal termination, signal number = %d%s.",

WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");

} else if (WIFSTOPPED(status)) {

log_info("child stopped, signal number = %d.", WSTOPSIG(status));

} else {

log_info("unknown exit code %d.", status);

}

exit(0);

}

测试场景

1. 正常退出

满足 WIFEXITED 宏定义的条件,也就是通过 exit() 或者 return 这种接口退出,此时可以直接通过 WEXITSTATUS 获取具体的错误码。

可以使用 bash -c "exit 1" 或者提供的 github exitcode 作为测试。

char *argv[] = {(char *)"/bin/bash", (char *)"-c", (char *)"exit 1", NULL};

char *argv[] = {(char *)REPO_PATH "/exitcode", (char *)"1", NULL};

注意,退出码只能是 8bits 也就是说 256 和 0 是相同的。

2. 异常退出

满足 WIFSIGNALED() 宏定义的判断条件。

Core 掉

常见的是除零错误 github coredump ,这里会有两种方式:A) 生成了 Core 文件,对应的返回码是 136 = 1000 1000;B) 没有生成 Core 文件,则是 8 = 1000 。

char *argv[] = {(char *)REPO_PATH "/coredump", NULL};

这里的 8 实际上对应了 SIGFPE 信号量。

是否生成 core file 文件可以通过 ulimit 命令进行查看 (ulimit -c)、开启 (ulimit -c unlimited)、关闭 (ulimit -c 0)。

发送信号

通过执行 sleep 1000 命令,然后通过 kill -SIGTERM 或者 kill -15 手动发送信号。

注意,如果注册了信号的回调函数,而在回调函数里是通过 exit() 退出的,那么实际上仍然被认为是 exit() 的退出方式。

3. 停止执行

实际上对应了 WIFSTOPPED() 宏定义,此时需要在子进程中调用 ptrace() 接口,默认返回的信号是 SIGTRAP 。

示例如下。

char *argv[] = {(char *)"/usr/bin/sleep", (char *)"1", NULL};

pid = fork();

if (pid < 0) {

log_error("fork failed, %s.", strerror(errno));

exit(EXIT_FAILURE); /* 1 */

} else if (pid == 0) { /* child */

ptrace(PTRACE_TRACEME, 0, NULL, NULL);

if (execvp(argv[0], argv) < 0) {

log_error("execl failed, %s.", strerror(errno));

return 0;

}

}

进程统计信息

在等待进程退出的时候,常用的有几个 API 调用,例如 wait() waitpid() wait3() wait4() 等,其中后两者还会获取到进程在运行时的一些统计信息。

实际上返回的是一个 struct rusage 结构体,也可以通过 getrusage() 函数获取当前进程的资源消耗。

如果喜欢这里的文章,而且又不差钱的话,欢迎打赏个早餐 ^_^

支付宝打赏

微信打赏

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值