linux进程退出之exit与_exit

_exit

/* Terminate program execution with the low-order 8 bits of STATUS.  */
/*
 * status参数定义了进程的终止状态,父进程可以通过wait()来获取该状态值。
 * 虽然status是int型,但是仅有低8位可以被父进程所用。
 */
extern void _exit (int __status) __attribute__ ((__noreturn__));

功能
调用_exit()函数会使程序立即退出,不会进行任何清理操作。用户调用_exit函数,本质上是调用exit_group系统调用。

exit

/* Call all functions registered with `atexit' and `on_exit',
   in the reverse of the order in which they were registered,
   perform stdio cleanup, and terminate program execution with STATUS.  */
/*
 1. exit()函数的最后也会调用_exit()函数,但是exit在调用_exit之前,还做了其他工作:
 2.   1)执行用户通过调用atexit函数或on_exit定义的清理函数。
 3.   2)关闭所有打开的流(stream),所有缓冲的数据均被写入(flush),通过tmpfile创建的临时文件都会被删除。
 4.   3)调用_exit。
 */
extern void exit (int __status) __THROW __attribute__ ((__noreturn__));

exit与_exit区别

流程

  1. 首先是exit函数会执行用户注册的清理函数。用户可以通过调用atexit()函数或on_exit()函数来定义清理函数。这些清理函数在调用return或调用exit时会被执行。执行顺序与函数注册的顺序相反。当进程收到致命信号而退出时,注册的清理函数不会被执行;当进程调用_exit退出时,注册的清理函数不会被执行;当执行到某个清理函数时,若收到致命信号或清理函数调用了_exit()函数,那么该清理函数不会返回,从而导致排在后面的需要执行的清理函数都会被丢弃

  2. 其次是exit函数会冲刷(flush)标准I/O库的缓冲并关闭流。
    glibc提供的很多与I/O相关的函数都提供了缓冲区,用于缓存大块数据。缓冲有三种方式:无缓冲(_IONBF)、行缓冲(_IOLBF)和全缓冲(_IOFBF)。

  • 无缓冲:就是没有缓冲区,每次调用stdio库函数都会立刻调用read/write系统调用。
  • 行缓冲:对于输出流,收到换行符之前,一律缓冲数据,除非缓冲区满了。对于输入流,每次读取一行数据。
  • 全缓冲:就是缓冲区满之前,不会调用read/write系统调用来进行读写操作。

对于后两种缓冲,可能会出现这种情况:进程退出时,缓冲区里面可能还有未冲刷的数据。如果不冲刷缓冲区,缓冲区的数据就会丢失。比如行缓冲迟迟没有等到换行符,又或者全缓冲没有等到缓冲区满。尤其是后者,很容易出现,因为glibc的缓冲区默认是8192字节。exit函数在关闭流之前,会冲刷缓冲区的数据,确保缓冲区里的数据不会丢失。

  1. 存在临时文件,exit函数会负责将临时文件删除。
  2. 调用_exit()函数。

清理函数

可以使用atexit()函数或on_exit()函数来注册清理函数,以在程序终止时执行一些特定的清理操作。

atexit()函数:

  • 原型:int atexit(void (*function)(void))
  • 作用:用于注册一个清理函数,该函数在程序正常终止时被调用。
  • 参数:function是一个指向无返回值的函数的指针,该函数没有参数。
  • 返回值:成功注册清理函数时返回0,失败时返回非零值。

以下是使用atexit()函数注册清理函数的示例:

#include <stdio.h>
#include <stdlib.h>

void cleanup1()
{
    printf("Cleaning up...1\n");
}

void cleanup2()
{
    printf("Cleaning up...2\n");
}

int main()
{
    printf("Main program starts.\n");

    // 注册清理函数
    atexit(cleanup1);
    atexit(cleanup2);

    printf("Main program ends.\n");

    exit(0);
}

运行结果:
在这里插入图片描述

在上述示例中,cleanup()函数被注册为清理函数。当程序正常终止时,清理函数会被自动调用。

on_exit()函数:

  • 原型:int on_exit(void (*function)(int status, void *arg), void *arg)
  • 作用:用于注册一个清理函数,该函数在程序终止时被调用,不论是正常终止还是异常终止
  • 参数:
    • function是一个指向接受两个参数的函数指针,第一个参数是终止状态码,第二个参数是可选的自定义参数。
    • arg是一个可选的自定义参数,会传递给清理函数。
  • 返回值:成功注册清理函数时返回0,失败时返回非零值。

以下是使用on_exit()函数注册清理函数的示例:

#include <stdio.h>
#include <stdlib.h>

void cleanup1() {
  printf("Cleanup 1 called\n");
}

void cleanup2(int status, void *arg) {
  printf("Cleanup 2 called with status: %d, arg: %p\n", status, arg);
}

int main() {
  // 使用 on_exit 函数注册终止处理函数
  on_exit(cleanup1, NULL);
  on_exit(cleanup2, (void *)0x12345678);

  printf("Main function executing\n");
  // 这里可以添加一些其他逻辑

  // return是一种更常见的终止进程的方法。
  // 执行return(n)等同于执行exit(n),因为调用main()的运行时函数会将main的返回值当作exit的参数。
  return 0;
}

运行结果:
在这里插入图片描述

在上述示例中,cleanup()函数被注册为清理函数,并且不接受自定义参数。当程序终止时,清理函数会被自动调用,并传递终止状态码作为参数。

推荐使用atexit,因为atexit是c标准函数,不同系统都会支持。而on_exit可能会在某些平台下无法支持,此函数来自 SunOS 4,但也存在于 libc4、libc5 和 glibc 中。它不再出现在 Solaris (SunOS 5) 中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值