glibc中_start、__libc_start_main、main、exit、init、finit、rtld_fini这几个函数的包含关系和调用先后顺序是什么

在 glibc 和一般的 Linux 程序执行流程中,以下是这几个函数的包含关系和调用顺序:

  1. _start:

    • 是程序执行的入口点,通常由编译器自动提供。
    • 它负责初始化程序,收集命令行参数以及环境变量,并准备调用 __libc_start_main
  2. __libc_start_main:

    • 这是 glibc 提供的启动例程,由 _start 调用。
    • 它负责更多的初始化工作,比如 I/O 设置、国际化支持、各种 lock 的初始化等。
    • 它也会设置程序退出时调用的 exit 函数。
    • 在这之后,__libc_start_main 会调用用户定义的 main 函数。
  3. main:

    • 用户编写的程序主体函数。
    • 执行完用户代码后,它会返回一个值通常表示程序退出状态。
  4. exit:

    • 用户的 main 函数返回后,__libc_start_main 会调用 exit 函数。
    • 它负责调用通过 atexit 或 on_exit 注册的清理函数。
    • 它会关闭所有的标准 C 库 I/O 流并清理临时文件等。
  5. init 和 fini(也称为__attribute__((constructor)) 和 __attribute__((destructor)) 函数):

    • init: 这些是用户定义的,或者在某些情况下是由特定库提供的初始化函数,它们在 main 函数执行之前调用。
    • fini: 这些函数在 exit 调用之前执行,是对应的终止函数,用于执行任何必要的清理工作。
  6. rtld_fini:

    • 这是动态链接器的清理函数,由 __libc_start_main 在 exit 之前调用。
    • 它是用于动态链接器清理其资源。

结合起来的调用关系和顺序如下:

_start() -> 初始化 -> 调用 __libc_start_main
    |
    |-> __libc_start_main(main, argc, argv, init, fini, rtld_fini, stack_end)
          |-> 调用 init 函数 (如果有的话)
          |-> 调用 main 函数
          |-> main 函数返回
          |-> 调用 fini 函数 (如果有的话)
          |-> 调用 exit 函数
              |-> 调用通过 atexit/on_exit 注册的所有终止处理函数
              |-> 调用 rtld_fini 清理动态链接器资源 (如果有的话)
              |-> 清理 C 库资源,关闭文件等
              |-> 进程终止,返回退出状态给操作系统
复制

以上是个高级概述;实际实现细节可能因具体的 Linux 发布版本和 glibc 版本而异。

 

除了你提到的函数外,进程启动和初始化过程中还涉及其他一些重要的函数和概念。让我们看看其中的一些:

  1. __libc_csu_init 和 __libc_csu_fini:

    • 这些函数通常由编译器生成,它们用于调用全局和静态构造器(init)和析构器(fini)。
    • __libc_csu_init 在 main 函数之前被调用,负责调用所有用 __attribute__((constructor)) 标记的函数。
    • __libc_csu_fini 在程序终止时调用,负责调用所有用 __attribute__((destructor)) 标记的函数。
  2. _init 和 _fini:

    • 这些是由链接器定义的特殊符号,通常与共享对象的初始化和终止相关。
    • 这些函数(如果存在)在进程启动和结束的时候自动被调用。
  3. _dl_start:

    • 在使用动态链接的程序中,_dl_start 是动态链接器的入口点。
    • 它负责为程序设置运行时链接,解析依赖库中的符号,等等。
  4. __gmon_start__:

    • 如果程序编译时启用了 gprof 性能分析,那么 __gmon_start__ 函数会在程序的初始化阶段被调用,用于初始化性能分析。
  5. __pthread_initialize_minimal:

    • 在多线程程序中,这个函数负责初始化线程库,设置主线程和其他与线程相关的基础设施。
  6. atexit 和 on_exit:

    • atexit 函数注册一个在程序正常退出时需要被调用的函数,exit 函数会调用这些注册的函数。
    • on_exit 类似于 atexit,但它还允许传递一个参数给注册的函数,它更老,不被推荐使用。
  7. __atexit_handler:

    • 这是 glibc 内部用来管理通过 atexit 注册的函数的处理程序。
  8. __do_global_dtors_aux__do_global_ctors_aux:

    • 这些是老版本 gcc 使用的函数,用于在程序退出时调用全局析构函数,以及在 main 函数之前调用全局构造函数。

启动流程中的很多函数都是在编译和链接阶段由工具链(如 GCC、ld、libc 等)自动注入到程序中的,不需要程序员直接写代码调用。这些过程大多是自动化的,但理解它们对于调试和优化程序启动时间,或解决复杂的动态链接问题是很有帮助的。

posted @ 2024-11-05 14:13  墨尔基阿德斯  阅读(28)  评论(0编辑  收藏  举报