CRT — C Runtime

C 语言的”零开销运行时”——操作系统与 main() 之间的薄胶水层。

概念

C Runtime (CRT) 是 C 程序执行的最小运行时环境。与 JVM 或 CPython 不同,CRT 几乎没有”重量级”服务——无 GC、无 JIT、无对象模型。它的核心职责是:把操作系统交给 _start,最终把控制权交给用户的 main,并在程序退出时完成清理。

核心组件

组件职责关联系统调用
启动 (crt0)_start__libc_start_mainmainexecve
堆管理malloc/free 通过 brk/mmap 向内核申请内存brk, mmap
栈帧管理调用约定 (calling convention) 维护 rbp/rsp
信号处理signal/sigaction 注册异步事件处理器sigaction, kill
程序终止atexit 注册退出回调, exit → _exitexit_group
线程局部存储TLS: __thread 变量, pthread 密钥arch_prctl
I/O 缓冲stdio FILE* 的用户态缓冲 (setvbuf)read, write

启动流程

KERNEL                        CRT                             USER
  |                             |                                |
  |--execve("a.out")---------->|                                |
  |                             |                                |
  |                    _start (crt0.S)                           |
  |                       设定栈指针                               |
  |                       收集 argc, argv, envp                   |
  |                       调用 __libc_start_main                  |
  |                             |                                |
  |                 __libc_start_main                            |
  |                   注册 atexit 回调                             |
  |                   __libc_init (TLS, 环境变量)                  |
  |                   调用 main(argc, argv, envp)                 |
  |                             |------------------------------->|
  |                             |              int main(...)     |
  |                             |                  ...           |
  |                             |<-------------------------------|
  |                             |            return 0            |
  |                   收集返回值                                   |
  |                   调用 exit(retval)                           |
  |                   遍历 atexit 回调链                           |
  |                   刷新 stdio 缓冲区                            |
  |                   调用 _exit(retval)                          |
  |<--_exit(retval)--------------|                               |

堆管理

// 简化版的 malloc 策略 (glibc ptmalloc)
void* malloc(size_t size) {
    if (size <= 64KB) {
        // 从线程本地 arena 分配
        // 使用 bins (fastbins, smallbins, largebins) 管理空闲块
        chunk = find_free_chunk_in_bins(size);
    }
    if (chunk == NULL) {
        // 通过 brk 扩展 data segment (小块)
        // 或通过 mmap 映射匿名页 (大块)
        chunk = request_from_kernel(size);
    }
    return chunk_to_mem(chunk);
}

信号处理

// 信号处理的用户态模型
struct sigaction {
    void (*sa_handler)(int);     // 处理函数指针
    sigset_t sa_mask;             // 信号处理期间屏蔽的信号
    int sa_flags;                 // SA_RESTART, SA_NODEFER 等
};
 
// 信号递送时内核在用户栈上构造一个栈帧
// RIP → sa_handler, RSP → 用户栈 (带 sigframe)
// 处理完毕后通过 sigreturn 系统调用恢复现场

与操作系统的关系

CRT 的本质是系统调用的 C 语言封装。malloc 不是系统调用——它是 brk/mmap 之上的分配器。fopen 不是系统调用——它是 open 之上的缓冲层。CRT 让 C 程序”感觉”像是直接操作硬件,但实际上 glibc/musl 做了大量簿记。