C语言运行时

✅ 在高级语言中,"运行时"包括两个重要含义:


程序运行期间的整个生命周期(你已经掌握)

这是"时间"上的意义,指从程序启动开始执行,到退出这一段时间。


运行时环境 / 运行时库(这是"组件"或"功能"上的意义)

指的是程序在运行时依赖的一组代码、机制或服务,叫做"运行时库"(runtime library)或"运行时系统"(runtime system)。


✅ 不同语言的运行时库示例

编程语言 对应的运行时 提供的功能示例
C/C++ CRT(C Runtime) main() 启动前的初始化、malloc()atexit()__libc_start_main()
Java JVM(Java Virtual Machine) 字节码解释、GC(垃圾回收)、类加载器、安全模型等
Python Python 解释器 + 标准库 动态类型管理、异常处理、模块加载、内存管理等
Go Go Runtime Goroutine调度、GC、channel 等并发原语支持
Rust core/std crate(部分) panic处理、栈展开、内存分配等(可选)

✅ 以 C 语言为例:运行时库 CRT 的作用

当你写一个简单的程序:

c 复制代码
#include <stdio.h>

int main() {
    printf("Hello\n");
    return 0;
}

虽然你只写了 main(),但实际执行前:

  1. 程序并不会直接从 main() 开始 ,而是从 _start 函数开始(由 CRT 提供);

  2. _start 会做以下操作:

    • 初始化堆栈;
    • 初始化 .data.bss(如果是裸机);
    • 调用构造函数(C++ 支持);
    • 调用 main()
    • 最后调用 exit(),完成清理工作;

这整个过程都是 运行时库(CRT)负责的


✅ 所以,"运行时"一词有两层含义:

含义 描述
时间层面 程序已经开始执行,从入口到退出这段时间
技术组件层面 支撑程序运行的底层库和机制,例如 malloc、GC、异常处理、线程调度等

如果你是嵌入式开发者,了解 C runtime 是非常有帮助的。如果你是做系统开发或高级语言(如 Python、Java)开发,这种"运行时"的理解会帮助你更清晰掌握程序行为背后的逻辑。

继续深入讲一下 C 程序执行背后的 _start → __libc_start_main → main 的全过程。需要吗?

非常好,深入了解 C 程序从 _start → __libc_start_main → main() 的执行流程,是理解 Linux 下程序运行机制的关键。下面我们分阶段详解它的全过程,包括涉及的关键符号、运行时库(CRT)、系统调用等。


🧭 总体流程图(Linux 下 C 程序运行)

复制代码
[ELF 加载器]
     ↓
 [入口: _start]                ← 由编译器 + glibc 提供
     ↓
[__libc_start_main()]          ← glibc 提供
     ↓
  调用你的 main()
     ↓
  main() 返回后,调用 exit()

1️⃣ _start:真正的程序入口点

  • _start 是链接器设置的默认入口点(通过 ENTRY(_start))。

  • 它是由 glibc 提供的,位于 crt1.o(C runtime startup object)中。

  • _start 做了很多事情,包括:

    • 设置 argcargvenvp
    • 调用 __libc_start_main(),传入 main() 和初始化/清理函数。

_start 源码原型(简化):

c 复制代码
void _start() {
    // setup stack, call __libc_start_main
    __libc_start_main(main, argc, argv, ..., init, fini, ...);
}

2️⃣ __libc_start_main():glibc 的运行时核心

这是整个 glibc 程序启动的"中控"。

它的主要工作:

  • 保存传入的 main() 函数指针;
  • 调用注册的构造函数(.init_array,比如 C++ 构造器);
  • 设置 TLS、locale 等环境;
  • 调用你的 main() 函数并传递参数
  • main() 返回后,调用 exit(),做清理工作。
c 复制代码
int __libc_start_main(
    int (*main)(int, char **, char **),
    int argc,
    char **argv,
    ... /* envp, init, fini, rtld_fini, stack_end */
);

🔧 注意:你在 Linux 上写的 main() 并不是程序的入口点,它只是被 __libc_start_main() 调用的一个函数。


3️⃣ main():你的业务逻辑

到了这一步,程序正式进入你编写的逻辑。你拿到 argcargv,做你要做的事。


4️⃣ main() 返回 → 调用 exit()

  • 如果你的 main() 返回,glibc 会调用 exit(),做以下工作:

    • 调用 .fini_array 中的析构函数(C++ 析构器等);
    • 调用 atexit() 注册的清理函数;
    • 向内核发出 exit() 系统调用,终结进程。

🔍 示例:使用 readelf 观察

bash 复制代码
$ readelf -s ./a.out | grep main
   34: 000000000040
相关推荐
GUET_一路向前4 分钟前
【C语言防御性编程】if条件常量在前,变量在后
c语言·开发语言·if-else·防御性编程
曳渔5 分钟前
UDP/TCP套接字编程简单实战指南
java·开发语言·网络·网络协议·tcp/ip·udp
三千道应用题21 分钟前
WPF&C#超市管理系统(6)订单详情、顾客注册、商品销售排行查询和库存提示、LiveChat报表
开发语言·c#·wpf
hqxstudying36 分钟前
JAVA项目中邮件发送功能
java·开发语言·python·邮件
咪咪渝粮39 分钟前
JavaScript 中constructor 属性的指向异常问题
开发语言·javascript
最初的↘那颗心40 分钟前
Java HashMap深度解析:原理、实现与最佳实践
java·开发语言·面试·hashmap·八股文
pusue_the_sun1 小时前
数据结构——栈和队列oj练习
c语言·数据结构·算法··队列
Dontla2 小时前
Makefile介绍(Makefile教程)(C/C++编译构建、自动化构建工具)
c语言·c++·自动化
后台开发者Ethan2 小时前
Python需要了解的一些知识
开发语言·人工智能·python
奶黄小甜包2 小时前
C语言零基础第18讲:自定义类型—结构体
c语言·数据结构·笔记·学习