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 做了很多事情,包括:

    • 设置 argc、argv、envp;
    • 调用 __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():你的业务逻辑

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


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

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

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

🔍 示例:使用 readelf 观察

bash 复制代码
$ readelf -s ./a.out | grep main
   34: 000000000040
相关推荐
2601_966949656 小时前
如何根据研究的起止时间截取分钟级行情?Python 实战与 QuantDash 数据处理指南
开发语言·python·pandas·量化交易·1024程序员节·股票数据·quantdash
晨曦中的暮雨8 小时前
限流算法——固定窗口、滑动窗口、漏桶、令牌桶
java·开发语言·算法
喵了几个咪8 小时前
GoWind CMS 拆解:在 Go 里给 TipTap 搭一套 Yjs 协同后端
开发语言·后端·golang
程序员清风8 小时前
数据处理脚本工程化:日志、配置、命令行与测试
服务器·开发语言·数据库
2601_9628857210 小时前
如何用 Python 把 K 线重采样成任意周期?
开发语言·python
Wang's Blog11 小时前
Java 项目实战: 交友项目 - 圈子功能实现:总结
java·开发语言·交友
下一站春风里11 小时前
位运算完整学习笔记:按位与(&)+ 异或(^)
开发语言·c++·算法
天外飞雨道沧桑12 小时前
我在浏览器里练了一个 792 参数的大模型,它学会了补全 JavaScript
开发语言·javascript·ai·ecmascript·ai编程
hhhjhl12 小时前
Yup 跨端三方库仓颉适配全流程
java·开发语言
运维行者_13 小时前
网络监控工具怎么选?从设备监控到智能运维的能力清单
运维·服务器·开发语言·网络·人工智能·支持向量机·php