glibc中_start、__libc_start_main、main、exit、init、finit、rtld_fini这几个函数的包含关系和调用先后顺序是什么
在 glibc 和一般的 Linux 程序执行流程中,以下是这几个函数的包含关系和调用顺序:
-
_start
:- 是程序执行的入口点,通常由编译器自动提供。
- 它负责初始化程序,收集命令行参数以及环境变量,并准备调用
__libc_start_main
。
-
__libc_start_main
:- 这是 glibc 提供的启动例程,由
_start
调用。 - 它负责更多的初始化工作,比如 I/O 设置、国际化支持、各种 lock 的初始化等。
- 它也会设置程序退出时调用的
exit
函数。 - 在这之后,
__libc_start_main
会调用用户定义的main
函数。
- 这是 glibc 提供的启动例程,由
-
main
:- 用户编写的程序主体函数。
- 执行完用户代码后,它会返回一个值通常表示程序退出状态。
-
exit
:- 用户的
main
函数返回后,__libc_start_main
会调用exit
函数。 - 它负责调用通过
atexit
或on_exit
注册的清理函数。 - 它会关闭所有的标准 C 库 I/O 流并清理临时文件等。
- 用户的
-
init
和fini
(也称为__attribute__((constructor))
和__attribute__((destructor))
函数):init
: 这些是用户定义的,或者在某些情况下是由特定库提供的初始化函数,它们在main
函数执行之前调用。fini
: 这些函数在exit
调用之前执行,是对应的终止函数,用于执行任何必要的清理工作。
-
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 版本而异。
除了你提到的函数外,进程启动和初始化过程中还涉及其他一些重要的函数和概念。让我们看看其中的一些:
-
__libc_csu_init
和__libc_csu_fini
:- 这些函数通常由编译器生成,它们用于调用全局和静态构造器(
init
)和析构器(fini
)。 __libc_csu_init
在main
函数之前被调用,负责调用所有用__attribute__((constructor))
标记的函数。__libc_csu_fini
在程序终止时调用,负责调用所有用__attribute__((destructor))
标记的函数。
- 这些函数通常由编译器生成,它们用于调用全局和静态构造器(
-
_init
和_fini
:- 这些是由链接器定义的特殊符号,通常与共享对象的初始化和终止相关。
- 这些函数(如果存在)在进程启动和结束的时候自动被调用。
-
_dl_start
:- 在使用动态链接的程序中,
_dl_start
是动态链接器的入口点。 - 它负责为程序设置运行时链接,解析依赖库中的符号,等等。
- 在使用动态链接的程序中,
-
__gmon_start__
:- 如果程序编译时启用了 gprof 性能分析,那么
__gmon_start__
函数会在程序的初始化阶段被调用,用于初始化性能分析。
- 如果程序编译时启用了 gprof 性能分析,那么
-
__pthread_initialize_minimal
:- 在多线程程序中,这个函数负责初始化线程库,设置主线程和其他与线程相关的基础设施。
-
atexit
和on_exit
:atexit
函数注册一个在程序正常退出时需要被调用的函数,exit
函数会调用这些注册的函数。on_exit
类似于atexit
,但它还允许传递一个参数给注册的函数,它更老,不被推荐使用。
-
__atexit_handler
:- 这是 glibc 内部用来管理通过
atexit
注册的函数的处理程序。
- 这是 glibc 内部用来管理通过
-
__do_global_dtors_aux
,__do_global_ctors_aux
:- 这些是老版本 gcc 使用的函数,用于在程序退出时调用全局析构函数,以及在
main
函数之前调用全局构造函数。
- 这些是老版本 gcc 使用的函数,用于在程序退出时调用全局析构函数,以及在
启动流程中的很多函数都是在编译和链接阶段由工具链(如 GCC、ld、libc 等)自动注入到程序中的,不需要程序员直接写代码调用。这些过程大多是自动化的,但理解它们对于调试和优化程序启动时间,或解决复杂的动态链接问题是很有帮助的。