glibc 源码内部机制和系统调用包装器

最后更新: 2026-05-07 03:47:31 +0800 +0800
原文以英文撰写。您正在阅读AI翻译版本。

glibc 暴露 read(2) 和 write(2) 等 C 函数,而 Linux 暴露特定于架构的系统调用入口约定。包装器连接这两层:它接受公共 C ABI,遵循所选架构的系统调用 ABI,转换内核错误,并且可能实现 POSIX 线程取消行为。

下面的示例描述当前上游源码树。已发布的 glibc 可能选择不同文件或宏定义,因此源码检查必须从正在构建的版本和目标三元组开始。

sysdeps/ 选择 #

sysdeps/ 树 保存按操作系统、CPU、ABI、字长和功能选择的实现。通用代码是回退实现;更具体的目录可以用相同相对名称的文件替换它。对于 Linux 构建,相关路径包括 sysdeps/unix/sysv/linux/;架构还可以提供 Linux 特定的入口代码或 ABI 特定变体。

glibc 的配置和构建机制会构建适用系统依赖目录的有序列表。构建规则搜索该列表,因此选定的实现会覆盖通用回退。该布局不是每个文件恰好只有一个维度的分类体系:操作系统目录可能依赖架构头文件,架构目录也可能包含与操作系统无关的代码。应检查 config.make、已配置的构建目录和选定的源代码路径,而不要从某个目录名推断行为。

生成的精简包装器 #

一些精简汇编包装器根据 syscalls.list 生成。每条记录命名一个输出、一个 Linux 系统调用、一个参数签名、一个强符号以及可选的弱符号或版本化名称。构建会调用 make-syscalls.sh,它会:

  1. 移除被更高优先级 sysdeps 目录覆盖的条目;
  2. 通过目标的 sysdep.h 解析 SYS_ 编号;
  3. 从签名推导参数数量和特殊参数处理;
  4. 使用 syscall-template.S 生成规则,并在列表要求时包含别名和兼容符号。

syscalls.list 不定义每个公共函数。带验证、回退逻辑、取消处理、时间宽度转换或其他策略的包装器通常是手写的。当前 glibc 中的 write 就是一个这样的手写 C 包装器,位于 write.c;因此它可以作为生成路径的对照。

write 和取消感知调度 #

从概念上看,Linux 的 write 包装器执行以下操作:

c
ssize_t
write(int fd, const void *buffer, size_t count)
{
    return cancellation_aware_syscall(SYS_write, fd, buffer, count);
}

实际源码调用 SYSCALL_CANCEL (write, fd, buf, nbytes)。当不需要取消检查时,该宏选择内联系统调用路径;否则,它会临时更改线程的取消类型,发起调用,然后恢复之前的类型。write 是 POSIX 取消点,因此该边界允许处理待处理的取消,而不会将每个内部系统调用都视为取消点。

宏会先按参数数量进行调度,然后才到达架构的内联系统调用原语。在 Linux 上,该原语将系统调用编号和参数放入目标 ABI 规定的寄存器,执行架构的系统调用指令,并接收内核返回值。C 函数 ABI 和寄存器顺序都不是通用的:x86-64、i386、AArch64 和其他端口使用不同的寄存器和破坏规则。

内核按照低层约定返回错误。选定的 glibc 支持代码识别错误范围,将正的错误编号存储在线程本地的 errno 中,并使正常的公共包装器返回 -1。有意暴露原始错误值的接口使用不同的内部约定。glibc 手册的系统调用章节记录了应用层契约;源码宏实现了特定于架构的边界。

公共名称、隐藏定义和别名 #

write.c 中的定义名为 __libc_write,随后源码声明内部可见性以及包括 __write 和公共 write 名称在内的别名。这种形式将实现身份与公共 ABI 身份分离:

  • libc_hidden_def 和 libc_hidden_weak 允许 glibc 内部调用在适当时绑定到隐藏定义,避免可插入的公共符号查找。
  • 强别名使另一个内部名称使用同一实现。
  • 当构建或 ABI 要求时,弱公共别名允许不同定义覆盖它。
  • 符号版本机制记录公共名称导出的 ABI 版本,并可以保留旧二进制文件的兼容入口。

hidden 会影响 glibc 内部的引用;它不会从共享库 ABI 中删除文档化的公共符号。诊断未解析符号时,使用 readelf --dyn-syms --version-info 检查架构的 libc.so,并将其与源码及发行版版本脚本比较。仅有别名声明不足以证明分发库导出的版本。

评论

在 GitHub 上阅读或评论(发表评论需要登录)