深入 C 语言和程序运行原理
于航
PayPal 技术专家
21121 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
深入 C 语言和程序运行原理
15
15
1.0x
00:00/00:00
登录|注册

29|C 程序的入口真的是 main 函数吗?

使用 main 函数返回值调用 exit 函数
调用 main 函数
调用 init 函数
注册 rtld_fini 和 fini 函数
初始化线程子系统
调用 __libc_start_main 函数进行进一步的准备工作
_start 函数负责准备 main 函数调用的参数
crt1.o 文件定义了 _start 的具体实现
crtbegin.o 和 crtend.o:提供构造函数与析构函数的代码实现
crti.o 和 crtn.o:提供共享对象的构造函数与析构函数能力
crt1.o:提供 _start 符号实现,参与可执行文件编译
平台相关,与操作系统紧密结合
提供启动与退出、C 标准库函数、IO、堆等支持
__libc_start_main 函数
可以通过链接器参数修改
链接器默认使用的符号
约定的程序入口名称
由链接器在生成 ELF 二进制文件时指定
_start 符号是实际的程序入口
如何理解 Linux Shell 中程序的处理过程?
CRT 在应用程序运行中扮演关键角色
_start 负责执行多项准备工作,保障应用程序正常运行
C 程序的实际入口不是 main 函数,而是 _start
CRT 相关对象文件
_start 有何作用?
_start 从何而来?
操作系统在执行 main 函数前进行多项准备工作
在机器指令层面,执行流程更为复杂
在日常开发中,main 函数总是优先执行
被认为是 C 程序的起始入口
思考题
总结
CRT (C Runtime Library)
真正的入口函数
程序执行流程
main 函数
C 程序的入口真的是 main 函数吗?

该思维导图由 AI 生成,仅供参考

你好,我是于航。
“main 函数是所有 C 程序的起始入口”,相信对于这句话,每个同学在刚开始学习 C 语言时都很熟悉,因为这是一个被各种教材反复强调的“结论”。但事实真是如此吗?
实际上,这句话对,但也不完全对。在一段 C 代码中定义的 main 函数总是会被优先执行,这是我们在日常 C 应用开发过程中都能够轻易观察到的现象。不过,如果将目光移到那些无法直接通过 C 代码触达的地方,你会发现 C 程序的执行流程并非这样简单。
接下来,我们先通过一个简单的例子,来看看在机器指令层面,程序究竟是如何执行的。

真正的入口函数

这里,我们首先在 Linux 系统中使用命令 “gcc main.c -o main” ,来将如下所示的这段代码,编译成对应的 ELF 二进制可执行文件。
// main.c
int main(void) {
return 0;
}
在上述代码中,由于没有使用到任何由其他共享库提供的接口,因此,操作系统内核在将其对应的程序装载到内存后,会直接执行它在 ELF 头中指定的入口地址上的指令。紧接着,使用 readelf 命令,我们可以获得这个地址。然后,通过 objdump 命令,我们可以得到这个地址对应的具体机器指令。
我将这两个命令的详细输出结果放在了一起,以方便你观察,如下图所示:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

C程序的入口并非完全是main函数,实际上是由链接器默认使用的_start符号所指代的。该符号在链接过程中被放入可执行文件的ELF头的e_entry字段中,由操作系统内核直接执行。文章详细解释了_start符号的由来和作用,以及通过GNU的C运行时库glibc为例,展示了_start的具体实现。在程序执行前,_start完成了对main函数地址、argc、argv等参数的准备工作,并调用了__libc_start_main函数,为程序的执行做了一系列前期准备工作。CRT为应用程序提供了对启动与退出、C标准库函数、IO、堆、C语言特殊实现、调试等多方面功能的实现和支持。CRT的实现是平台相关的,与具体操作系统结合得非常紧密。文章通过深入分析程序执行的底层机制,揭示了C程序入口的真相,为读者提供了深入理解程序执行过程的视角。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入 C 语言和程序运行原理》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(2)

  • 最新
  • 精选
  • 一个工匠
    操作系统对 Shell 的执行,是靠 Shell 解释器完成的。在操作系统运行后,Shell 解释器本身就加载并运行了。其中如 pwd,cd 这些是内部命令,本质是函数调用,可以直接使用。ls 这些是外部命令,需要 fork 一个新进程执行当前命令。一个shell脚本,有很多个这些内外部命令组成,通过 shell 解释器逐行解释完毕后执行。shell 解释器也是一个应用程序,本质是一个 C 程序,不过在该程序中,手动模拟了函数调用栈,和 JVM 有相似之处。所以 shell 解释器,也有静态库/动态库/静态链接/动态链接这些,为 shell 命令的执行保障护航。

    作者回复: 回答的很赞!

    2022-03-30
    8
  • 神佑小鹿
    shell 本身就是个程序是运行在 Linux 上的进程,shell 内部命令的执行是在对应的 shell 进程内运行的,外部命令是不同的,要 fork 一个新进程~~
    2022-05-15
    1
收起评论
显示
设置
留言
2
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部