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
相关推荐
扶摇直上——————10 分钟前
C专题8:文件操作2
c语言·文件操作
Vertira28 分钟前
python 阿里云 安装 dashscope的简介、安装
开发语言·python
我爱学嵌入式2 小时前
C语言第 9 天学习笔记:数组(二维数组与字符数组)
c语言·笔记·学习
hqxstudying2 小时前
Java异常处理
java·开发语言·安全·异常
wjs20245 小时前
状态模式(State Pattern)
开发语言
我命由我123455 小时前
Kotlin 数据容器 - List(List 概述、创建 List、List 核心特性、List 元素访问、List 遍历)
java·开发语言·jvm·windows·java-ee·kotlin·list
liulilittle5 小时前
C++ TAP(基于任务的异步编程模式)
服务器·开发语言·网络·c++·分布式·任务·tap
励志要当大牛的小白菜7 小时前
ART配对软件使用
开发语言·c++·qt·算法
爱装代码的小瓶子9 小时前
数据结构之队列(C语言)
c语言·开发语言·数据结构
Maybe_ch10 小时前
.NET-键控服务依赖注入
开发语言·c#·.net