从原始系统调用到弄清 NSS 与 libc 的疯狂之旅
Source: Dev.to
我一直对 Linux 抱有一种书呆子式的执着。从第一天起,我就全身心投入到内核中,使用 raw syscalls 编写小工具——完全跳过 libc,直接与内核对话。我自己写头文件,自己实现 printf‑风格的函数,用 getdents64 打开目录,解析 /proc 获取进程信息……基本上就是在内核的后院里冒险。
当时我觉得自己 牛逼,把一切都做到“真正的方式”。它能跑,但我跳过了 libc 中 整整一层理智。我根本不知道它实际上做了多少事:缓冲、可读的格式化以及其他让生活不那么痛苦的便利功能。
The WTF Moment: /etc/nsswitch.conf
Everything hit me when I stumbled upon /etc/nsswitch.conf while poking around network and user stuff. I opened it and literally went:
“这到底是怎么回事?谁他妈会用这个?难道内核不应该自己知道吗?”
I was in full raw‑syscall mode, so the idea that 某个配置文件 could tell libc how to handle getpwnam() or getaddrinfo() blew my mind. I had no clue why this “sign‑board” was even necessary.
好了,现在 NSS 有意义了
原来,NSS = Name Service Switch。它基本上是 在 libc 内的一个招牌,告诉它到底去哪里查找信息。
当程序调用类似 getpwuid() 或 getpwnam() 时,它在询问:
“这个用户是谁?他的用户名是什么?”
内核 并不知道用户名;它只知道数字 ID(例如,UID = 1000)。这时 libc + NSS 就像超级英雄一样出现。
实际工作原理
假设你调用 getpwnam("Deadpool")。libc 大致执行以下步骤:
-
读取
/etc/nsswitch.conf
找到用户对应的行:passwd: files systemd这表示:
- 首先,检查本地文件(
/etc/passwd) - 然后,查询 systemd 的动态用户数据库
- 首先,检查本地文件(
-
按顺序调用每个 NSS 模块
-
libnss_files.so.2→ 在/etc/passwd中查找- 找到?→ 返回结果
- 未找到?→ 继续
-
libnss_systemd.so.2→ 向 systemd 询问- 找到?→ 返回结果
- 未找到?→ 继续(如果还有其他来源)
-
-
返回一个
struct passwd给你的程序,里面包含用户名、UID、GID、主目录、Shell 等信息。
重要提示: NSS 本身 不是一个运行中的程序;它只是一个配置文件加上一组共享库。
libc动态加载这些库,因此不会涉及额外的进程。
秘密酱汁:libc 超越原始系统调用
libc 不仅仅是系统调用的薄包装。的确,它包装了 write、read、open、getpid 等调用,但它还加入了大量额外功能:
| 类别 | 示例 |
|---|---|
| 缓冲与格式化 | printf, fprintf, fopen, getline |
| 内存管理 | malloc, free, realloc |
| 线程支持 | pthread_* 函数 |
| 策略与查找逻辑 | NSS 模块、区域设置处理、主机名解析 |
如果没有 NSS,这些额外的酱汁只能部分工作:
- 系统调用仍然会正确执行(
open、read、write,……) - 缓冲、格式化、内存、线程仍然可用
- 任何需要人类可读名称的函数(用户、组、主机、服务)将会 失败或返回
NULL,因为libc没有映射告诉它去哪里查找。
NSS Modules Still Use Syscalls Under the Hood
Even NSS modules ultimately rely on syscalls:
libnss_files.so.2reads/etc/passwdusingopen,read,close.libnss_systemd.so.2talks to the systemd service via IPC syscalls.
So everything still hits the kernel; NSS just adds a layer of logic so libc knows where and how to get the data.
Bottom line:
libc+ NSS = fancy middleware. Raw syscalls = truth. NSS gives meaning to that truth and ensures humans don’t see useless numbers.
这让我产生的感悟
在顿悟之前,我的工具会做以下事情:
- 使用
getdents64打开目录 - 读取
/proc/<pid>/status→ 获取数值 UID - 仅显示数字
如果你只想要原始事实,这样做没问题,但对人类来说很无聊。人类想要的是:
PID: 742 User: Deadpool Process: bash
而不是:
PID: 742 UID: 1000 Process: bash
NSS 基本上把事实翻译成意义,而 libc 完成所有繁重的工作。
系统层次(心理图)

我学到的关键要点
libc不仅仅是包装系统调用:缓冲、格式化、线程、内存,以及 NSS。- 如果只需要数字,NSS 是可选的,但人类会讨厌你的输出。
- Systemd 为服务和容器提供动态用户数据库,NSS 可以查询它。
- 原始系统调用 = 真相;NSS = 含义。
我的反思
实话实说:这是一次巨大的“哎呀”时刻。我的低层工具虽然很酷,但并不真正“用户友好”或“系统感知”。我一直在跳过整个用户空间逻辑的生态系统。
现在我看到将两者结合的价值:
- 原始系统调用 → 像高手一样理解内核和 ABI。
libc+ NSS → 制作可读、稳健且真正有用的工具。
One‑Liner to Summarize
“系统调用给你原始真相;NSS 给
libc那该死的映射。”以将该真相转化为人类真正能理解的东西。