目录遍历、文件事件与权限¶
这一章讲什么: 把「文件系统」从一层文件名皮扒到内核对象:每个文件真正的身份是 inode, 权限、属主、时间戳全存在里面;文件名只是目录表里给人看的条目。 三个工具串起全章:ztree(递归画出目录树)、zwatch(内核推送的文件事件监控)、 iChmod/zchown(改权限和属主)。
主线例子:zwatch /tmp。在 macOS 上你会看到
Changes detected in directory (fd: 4), 在 Linux 上你会看到Event detected on: newFile——同一句话的两版答案,差异本身就是这一章要讲的事。
1. 先立地基:inode、vnode 与权限位¶
UNIX 文件系统的心是 inode(索引节点):文件的身份证明。文件名只是目录表里的可读条目,inode 才装着真东西——文件大小、数据在磁盘块上的物理位置、权限位;你改文件的权限或属主,改的不是文件内容,而是 inode 里的字段1。
后来操作系统要同时支持多种文件系统,内核又加了一层抽象:vnode(虚拟节点)。inode 是绑在具体磁盘上的静态记录,vnode 是内存里的动态对象,由虚拟文件系统(VFS)层管理——进程打开文件时内核建一个 vnode,把通用的 read/write 翻译成具体文件系统的操作。macOS 和 BSD 的事件监控子系统盯的就是 vnode2。
权限模型三根轴:属主(Owner)、属组(Group)、其他人(Others),每轴 3 位——读 4、写 2、执行 1。八进制 755(二进制 111 101 101)= 属主全权,组和其他人只能读和执行;Zig 里用 std.posix.mode_t 类型和这套位打交道3。一个容易混的细节在后面会用到:元数据里打出来的权限常是 0o40755 这种——前缀 40 是文件类型(目录 = S_IFDIR = 0o040000,占第 12–15 位),755 才是权限位,两者不能互换4。
符号链接是另一种文件类型:内容是一段文本路径。内核解析路径时碰到它就把操作转向目标路径。和硬链接(只是同一 inode 的另一个名字)不同,symlink 是独立文件。这带来一个工程上要紧的区分:lstat(2) 停在链接本身、报告链接的类型;open/stat 会穿透链接、报告目标的类型——任何要区分「链接」和「链接目标」的工具都必须走 lstat 这条路5。
2. 顶层全景:zwatch 的两副面孔¶
应用: zwatch /tmp
│
注册阶段 │
├─ Linux: inotify_init1() 得一个队列 fd
│ inotify_add_watch(fd, "/tmp", MODIFY|CREATE|DELETE)
│
└─ macOS: kqueue() 得一个队列 fd
openat("/tmp") 得目录 fd(必须保持打开!)
kevent 注册: EVFILT_VNODE + NOTE_WRITE|DELETE|RENAME
│
等待阶段 │ 阻塞在 read(fd)/kevent() 上 —— 文件系统安静时 CPU 占用≈0
│
事件到达 ├─ Linux: read 返回一串「定长头 + 变长文件名」,指针算术逐条剥
│ 输出: Event detected on: newFile ← 有文件名
│
└─ macOS: kevent 返回定长 kevent 数组
输出: Changes detected in directory (fd: 4) ← 只有 fd
图说:inotify 是事件中心的 API(告诉你哪个文件发生了什么),kqueue 是描述符中心的 API(告诉你某个 fd 上有动静)。6
3. 核心原理¶
3.1 路径解析:三种写法,一次交叉验证¶
把相对路径解析成绝对路径(展开 .、..、解析沿途符号链接)叫规范化(canonicalization),是一切文件操作的前置——文件能被打开之前,路径必须先被解析;symlink 能被安全跟随之前,目标必须先对着已知基准解析7。
书上并排给了三条路: C 的 realpath();Zig 的 realPathFile()(填调用方给的定长缓冲,全程栈上零分配);realPathFileAlloc()(动态申请,路径多长都不怕)8。选型就一句话:路径长度能保证不超过 max_path_bytes、又在热循环里——用定长栈缓冲;通用程序——用 Alloc 版;两者都优先于 C 版,安全和依赖都更干净9。三条路跑同一个输入、输出同一个绝对路径,这本身就是一次交叉验证10。
3.2 列目录与认类型:一个开关决定你看的是谁¶
列目录的钥匙是一个容易被忽略的开关:openDir(…, .{ .iterate = true })——它指示内核把目录准备好「流式出条目」而不是只给简单访问;不设这个标志,后面 iterate() 根本调不动11。迭代器每次吐一个条目,不分配内存。
认类型(普通文件/目录/符号链接)用的是元数据的 kind 字段,但关键是 statFile 的 .follow_symlinks = false 选项:不设它,底层调用会穿透符号链接、报告目标的类型,于是 .sym_link 分支永远走不到——这正是 POSIX 里 open 与 lstat 的区别,任何要区分链接与目标的文件系统工具都必须走 lstat 这条路12。
元数据一次 stat() 全取回来:大小、权限、三个时间戳(atime 访问、mtime 内容修改、ctime 元数据变化)、类型。du 按块求和、find 按时间和大小过滤、备份系统用 mtime/ctime 判断增量——数据永远是同一层,工具的区别只在于怎么解读和行动13。
3.3 ztree:递归、排序、和前缀的艺术¶
ztree 的核心是一个递归函数 printTree(),签名里就藏着全部设计:当前目录句柄、递归深度(供 -L 限制)、前缀字符串(画连接线)、是否显示隐藏文件14。三个机制值得点名:
- 先收集再排序:目录迭代器吐条目的顺序由底层文件系统决定,不可预期;要得到稳定的树形输出,必须把条目收进
ArrayList按名排序后再打印15。 - 前缀累积:每条目按「是否最后一个」选连接器(
├──或└──),下钻时前缀追加│(还有兄弟)或(没有),树的对齐就靠这一串空格和竖线传下去16。 - 符号链接只显示、不深入:条目是符号链接时,用
readLink()解析出目标显示成lib -> ../lib64,但绝不递归进去——因为符号链接可能成环(某目录里有个指回自己祖先的链接),盲目跟随会让程序死循环。tree(1)默认也是这个策略;要支持跟进,生产工具得跟踪已访问 inode、撞见就停,书把这份复杂留作练习17。
输出长这样(-L 1 只下钻一层)18:
3.4 平台分支在编译期就剪干净¶
zwatch 之前要解决「现在是什么操作系统」。Zig 的答案是 builtin.os.tag——编译期常量,if/else if/else 在编译阶段求值,不命中的分支直接被剪出最终二进制;else 里放 @compileError("Unsupported operating system") 当编译期守卫:不支持的平台根本连构建都过不了,把平台差异强制摊在开发者面前,而不是留给用户机器去撞19。
3.5 事件模型:从轮询到内核推送¶
文件事件是内核在文件系统对象的生命周期发生转变(写入、改名、删除)时发出的通知,通过一个专门的文件描述符(一个队列)投递:程序预先注册一个「掩码/过滤器」,内核匹配到就把事件结构推进队列;程序用阻塞读消费这个队列——文件系统闲时进程休眠,CPU 占用几乎为零20。这比手动轮询(每秒查一次时间戳)根本地省:轮询的代价在闲时也要付,事件驱动把代价压到零。
3.6 Linux:inotify,变长事件的指针算术¶
inotify 的初始化两步:inotify_init1() 拿一个队列 fd(所有目录的事件都 multiplex 到这一个描述符);对每个目录 inotify_add_watch(fd, 路径, IN.MODIFY | IN.CREATE | IN.DELETE),位掩码精确告诉内核对 inode 的哪些变化感兴趣21。
等待循环阻塞在 read(fd, &buf)。读回来的 buffer 里是一个或多个事件,每个都是「定长头 + 零结尾文件名」的变长结构——这是第 3 章指针算术的实战:缓冲区声明时对齐到 inotify_event;每轮用 @ptrCast(@alignCast(&buf[i])) 把当前偏移 reinterpret 成事件指针;文件名在头之后 @sizeOf 处,std.mem.span 把零结尾指针变回 Zig 切片;最后 i += @sizeOf(头) + event.len 精确跳下一条22。跑起来,在 /tmp 里 touch 一个文件,输出 Event detected on: newFile——内核直接把肇事文件名递到你手里23。
3.7 macOS:kqueue,描述符的看守人¶
kqueue 是严格描述符导向的:它看守的是打开的文件描述符,所以程序得先自己 openat() 每个要看的目录,而且这些 fd 在监控期间必须保持打开——内核靠描述符的生命周期追踪被监视的 vnode(代码注释也承认:生产工具应该在收到 SIGTERM/SIGINT 时统一收尸这些 fd)24。
每个目录包一个 kevent 结构:filter = EVFILT_VNODE(看文件系统级变化),flags = EV_ADD | EV_ENABLE | EV_CLEAR(注册、启用、触发后自动清除),fflags = NOTE_WRITE | NOTE_DELETE | NOTE_RENAME(报告写、删、改名)25。
kevent() 一个函数两种用法:第一次传入事件数组、传出参数为空——批量注册,立即返回;之后传入空、传出数组——进入等待模式,阻塞到任一注册的变化发生,内核把定长 kevent 结构填进你的数组26。跑起来的输出是 Changes detected in directory (fd: 4)——4 就是你打开 /tmp 拿到的那个描述符,事件只告诉你「这个 fd 上发生了写」,不告诉你是谁27。
两副面孔的对照就是这一节的落点:要拿到文件名,inotify 免费给你;kqueue 要补一层「状态对比」——收到 NOTE_WRITE 后重扫目录、和注册时存的快照比差异(书上把这个实现留作练习,提示用 std.StringHashMap 存每个 fd 的文件名集合)28。
3.8 iChmod 与 zchown:位和身份的写入¶
iChmod 三行核心:把 "755" 按八进制解析成整数(为什么是 8?UNIX 权限惯例就是八进制);打开文件拿到描述符;std.c.fchmod() 直接调 C 的 fchmod(2)——不需要 @cImport,Zig 通过 std.c 直接暴露了这个函数29。
zchown 多走一步:它要 @cImport unistd.h/fcntl.h 拿 fchownat(2) 和常量,把 UID/GID 按十进制解析,路径用 bufPrintZ 造一个零结尾副本(第 3 章那套 C 字符串规矩在这里又出现一次),然后 fchownat(AT_FDCWD, path, uid, gid, 0):AT_FDCWD 让路径相对当前目录解析,flag 为 0 表示跟随符号链接。错误路径做得讲究:失败时把 errno 转成 Zig 错误枚举、格式化成 zchown: error: Xxx 打到 stderr、退出码 130。实测:zchown 501 20 /tmp/testFile 之后 ls -l 显示 mtsouk staff——数字 ID 是系统调用语言,用户名是 ls 查 /etc/passwd 翻译给人看的31。
4. 加厚:四个容易一眼带过的机制¶
4.1 rename 为什么是同分区才有的免费午餐¶
mv 的核心是 rename(2):同分区时,内核只做一件事——删掉旧名字、建一个指向同一 inode 的新目录项,数据块原地不动,O(1),与文件大小无关;目标已存在也在单次事务里被替换,任何其他进程不会看到「搬到一半」的状态33。跨分区则系统调用直接失败(EXDEV,无效跨设备链接),应用必须退化成「手动拷数据 + 删原件」——这也是为什么大文件搬家跨盘时慢,而同盘改个名字永远瞬时的全部原因34。
4.2 删除的真相:unlink 不是擦除¶
deleteFile() 底下是 unlink(2),这个名字比「删除」诚实:它不一定擦数据——它摘掉文件名这个目录项,把 inode 的链接计数减一;计数归零且没有任何进程还开着这个文件,内核才把磁盘块标为可复用35。文件被删了却还在被进程持有的场景(日志切割、运行中的二进制自更新)全靠这个语义兜底——「删了就消失」是表象,「引用计数归零才真正回收」才是机制。
4.3 时间戳与权限位怎么读¶
file.stat() 一调带回全部:大小、权限、三个时间戳。atime 是最后访问,mtime 是最后改内容,ctime 是最后改元数据——备份系统拿 mtime/ctime 判断增量,审计工具拿 ctime 发现「内容没变但权限被动过」36。打印用 {o} 格式化输出八进制,这是 UNIX 权限的传统写法;0o40755 里前缀 40 是文件类型位(目录),755 才是权限,程序里读写模式值时两者不可互换37。
4.4 错误的人格:DirNotEmpty 与调试期栈¶
删非空目录,rmdir(2) 直接报 error.DirNotEmpty,Debug 模式下错误沿调用栈传播回 main,运行时打印一行回溯——这不是 bug,是 Debug 构建的预期行为;发布版构建里回溯被抑制,只剩应用自己的一行话。生产工具的体面做法是在顶层接住错误、打印人话、std.process.exit(1),而不是把原始错误抛给终端38。
4.5 zwatch 的两个 registration 细节¶
inotify 注册用的是位掩码的"或":IN.MODIFY | IN.CREATE | IN.DELETE——位掩码精确告诉内核对 inode 的哪些状态变化感兴趣,单个队列 fd multiplex 全部目录,内核替应用扛住跨文件系统的跟踪重活39。kqueue 的注册多一步手工:kevent 结构里 ident 填目录 fd、filter 填 EVFILT_VNODE(文件系统级)、flags 填 EV_ADD | EV_ENABLE | EV_CLEAR(注册、启用、触发后自动清除),fflags 填 NOTE_WRITE | NOTE_DELETE | NOTE_RENAME(写、删、改名)40。EV_CLEAR 的"触发即自清"意味着每次事件都要重新武装——换来的是不会有重复通知堆积。
消息缓冲也有一个低耗姿势:zwatch 用一块 512 字节的栈上 msg_buf 配合 bufPrint 反复格式化状态行——不申请新堆内存,监控循环的 setup 阶段也保持轻量41。
5. 作者的判断与证据¶
- 判断: 自包含的小程序是学系统编程的正道——「逻辑、数据结构、系统调用全在一个可读的源文件里,没有隐藏配置、没有不透明依赖」,而且这样的程序多年以后更可能还编译得过,哪怕语言本身又有破坏性变更32。这是全书的方法论宣言,也解释了为什么每章都是一堆独立小工具而不是一个大框架。
- 判断: 事件驱动优于轮询,因为闲时代价归零20。证据:inotify/kqueue 的阻塞读模型本身;但没有给轮询 vs 事件的实测对比数字。
- 判断(隐含): kqueue 的「描述符管理负担」是这套 API 的代价——每个被看对象一个 fd、生命周期自己管24。证据:代码注释里自己点出的 SIGTERM 清理问题;这其实是承认示例在生产度上有缺口。
5. 边界与局限¶
- zwatch 不递归监控子目录(注册几个目录看几个),也没有 kqueue 路径下的文件名识别——后者明说是练习28。
- kqueue 分支泄漏被看目录的 fd:示例只在监控结束时 close 队列 fd,书用注释承认生产工具需要信号处理来收尸24(第 7 章才讲信号)。
- ztree 不跟踪已访问 inode,跟随符号链接会死循环——书明说这是故意的安全默认,复杂版留给读者17。
- zchown/iChmod 直接调 C(
std.c/@cImport),Windows 不适用;builtin.os.tag的编译期守卫只在「不支持就拒建」的意义上跨平台19。 - 权限位那节的
0o{o}输出依赖 Zig 的八进制格式化,时间戳打出来是裸纳秒数,没有做人可读转换4。
6. 可带走的¶
- 文件名是皮,inode 是里;改权限/属主 = 改 inode 字段,不碰数据。
- 权限记三根轴(主/组/他)× 三位(4/2/1);注意
0o40755里40是文件类型、755才是权限。 - symlink 是文件不是别名;区分链接和目标必须走 lstat(
follow_symlinks = false)。 openDir不带.iterate = true就没法迭代——这个开关坑过不止一个人。- 规范化路径:热路径用
realPathFile栈缓冲,通用代码用Alloc版;三条路跑同输入可做交叉验证。 - 递归画树三件套:收集排序保稳定、前缀累积保对齐、符号链接只看不进防死环。
builtin.os.tag是编译期常量,不命中分支不进二进制;else里@compileError当平台守卫。- 事件驱动 vs 轮询:闲时代价归零,阻塞读就是全部等待逻辑。
- inotify:一个队列 fd multiplex 所有目录,事件是「定长头+变长名」,指针算术逐条剥。
- kqueue:看的是 fd 不是路径,每个被看对象一个 fd 且必须保活;给不出文件名,想要就自己快照对比。
std.c直接暴露了一批 POSIX 函数(fchmod 等),不是每次都要@cImport。- 数字 UID/GID 是内核语言;
ls显示的 mtsouk:staff 是查库翻译。
7. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| inode 与权限位 | Understanding inodes, file permissions, and symlinks | text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:11(搜「index node」) |
| vnode 与 VFS | Understanding inodes, file permissions, and symlinks | text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:35(搜「virtual node」) |
| 三轴权限模型 | Understanding inodes, file permissions, and symlinks | text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:63(搜「three distinct categories」) |
| symlink vs 硬链接、lstat | Understanding inodes, file permissions, and symlinks | text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:143(搜「simply another name」) |
| 规范化与三条解析路 | Resolve a relative path to its absolute counterpart | text/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:9(搜「canonicalization」) |
| 栈缓冲 vs Alloc 的选择 | Resolve a relative path to its absolute counterpart | text/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:130(搜「fixed-buffer approach」) |
| iterate = true 开关 | Listing directory entries | text/58-fm-listing-directory-entries.txt:75(搜「prepare the directory」) |
| follow_symlinks = false | Finding the kind of file | text/59-fm-finding-the-kind-of-file.txt:81(搜「important here」) |
| 元数据一调全取 | Finding file metadata | text/60-fm-finding-file-metadata.txt:29(搜「the tool is defined」) |
| 0o40755 的类型前缀 | Finding file metadata | text/60-fm-finding-file-metadata.txt:174(搜「0o40755」) |
| rename 同分区 O(1) | Moving a file | text/64-fm-moving-a-file.txt:9(搜「same physical partition」) |
| ztree 递归签名 | Recursively walking directories | text/65-fm-recursively-walking-directories.txt:29(搜「printTree」) |
| 收集排序保稳定 | Recursively walking directories | text/65-fm-recursively-walking-directories.txt:141(搜「deterministic」) |
| 符号链接不深入 | Recursively walking directories | text/65-fm-recursively-walking-directories.txt:191(搜「deliberate safety decision」) |
| ztree 实测输出 | Developing the utility | text/66-fm-developing-the-utility.txt:219(搜「LICENSE」) |
| 编译期平台分支 | Finding out the active operating system | text/67-fm-finding-out-the-active-operating-system.txt:48(搜「compile-time constant」) |
| 事件队列模型 | Understanding file events | text/68-fm-understanding-file-events.txt:13(搜「queue」) |
| inotify 注册两步 | The event loop: polling for changes | text/69-fm-the-event-loop-polling-for-changes.txt:141(搜「centralized queue」) |
| 变长事件指针算术 | The event loop: polling for changes | text/69-fm-the-event-loop-polling-for-changes.txt:235(搜「variable-length」) |
| kqueue 的 fd 保活负担 | The event loop: polling for changes | text/69-fm-the-event-loop-polling-for-changes.txt:340(搜「remain open」) |
| kevent 两用 | The event loop: polling for changes | text/69-fm-the-event-loop-polling-for-changes.txt:437(搜「two distinct purposes」) |
| 两平台输出对照 | The event loop: polling for changes | text/69-fm-the-event-loop-polling-for-changes.txt:520(搜「fd: 4」) · :558(搜「newFile」) |
| kqueue 文件名练习 | The event loop: polling for changes | text/69-fm-the-event-loop-polling-for-changes.txt:1101(搜「snapshot」) |
| iChmod 八进制解析 | The event loop: polling for changes | text/69-fm-the-event-loop-polling-for-changes.txt:677(搜「base 8」) |
| zchown 数字 ID 与 ls 翻译 | The event loop: polling for changes | text/69-fm-the-event-loop-polling-for-changes.txt:987(搜「501」) |
| 「自包含小程序」方法论 | The event loop: polling for changes | text/69-fm-the-event-loop-polling-for-changes.txt:1025(搜「longevity」) |
本章拆解依据原书第 5 章(含各小节)。每条出处可用 node scripts/book-verify.mjs systems-programming-with-zig 回核。
-
出处:「Understanding inodes, file permissions, and symlinks」第 11 段(
text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:11,搜「index node」)。 ↩ -
出处:「Understanding inodes, file permissions, and symlinks」第 35 段(
text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:35,搜「virtual node」)。 ↩ -
出处:「Understanding inodes, file permissions, and symlinks」第 63 段(
text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:63,搜「three distinct categories」)与第 99 段(text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:99,搜「3-bit value」)。 ↩ -
出处:「Finding file metadata」第 174 段(
text/60-fm-finding-file-metadata.txt:174,搜「0o40755」)。 ↩↩ -
出处:「Understanding inodes, file permissions, and symlinks」第 143 段(
text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:143,搜「simply another name」)。 ↩ -
出处:「Understanding file events」第 41 段(
text/68-fm-understanding-file-events.txt:41,搜「specific name of the affected file」)与「The event loop: polling for changes」第 67 段(text/69-fm-the-event-loop-polling-for-changes.txt:67,搜「event-centric API」)。 ↩ -
出处:「Resolve a relative path to its absolute counterpart」第 173 段(
text/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:173,搜「foundational operation」)。 ↩ -
出处:「Resolve a relative path to its absolute counterpart」第 9 段(
text/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:9,搜「canonicalization」)。 ↩ -
出处:「Resolve a relative path to its absolute counterpart」第 130 段(
text/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:130,搜「fixed-buffer approach」)。 ↩ -
出处:「Resolve a relative path to its absolute counterpart」第 171 段(
text/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:171,搜「converge on the same absolute path」)。 ↩ -
出处:「Listing directory entries」第 75 段(
text/58-fm-listing-directory-entries.txt:75,搜「prepare the directory」)。 ↩ -
出处:「Finding the kind of file」第 81 段(
text/59-fm-finding-the-kind-of-file.txt:81,搜「important here」)与第 109 段(text/59-fm-finding-the-kind-of-file.txt:109,搜「must take the」)。 ↩ -
出处:「Finding file metadata」第 29 段(
text/60-fm-finding-file-metadata.txt:29,搜「the tool is defined」)。 ↩ -
出处:「Recursively walking directories」第 29 段(
text/65-fm-recursively-walking-directories.txt:29,搜「printTree」)。 ↩ -
出处:「Recursively walking directories」第 141 段(
text/65-fm-recursively-walking-directories.txt:141,搜「deterministic」)。 ↩ -
出处:「Recursively walking directories」第 155 段(
text/65-fm-recursively-walking-directories.txt:155,搜「new」)。 ↩ -
出处:「Recursively walking directories」第 191 段(
text/65-fm-recursively-walking-directories.txt:191,搜「deliberate safety decision」)。 ↩↩ -
出处:「Developing the utility」第 219 段(
text/66-fm-developing-the-utility.txt:219,搜「LICENSE」)。 ↩ -
出处:「Finding out the active operating system」第 48 段(
text/67-fm-finding-out-the-active-operating-system.txt:48,搜「compile-time constant」)。 ↩↩ -
出处:「Understanding file events」第 17 段(
text/68-fm-understanding-file-events.txt:17,搜「zero CPU cycles」)。 ↩↩ -
出处:「The event loop: polling for changes」第 141 段(
text/69-fm-the-event-loop-polling-for-changes.txt:141,搜「centralized queue」)。 ↩ -
出处:「The event loop: polling for changes」第 235 段(
text/69-fm-the-event-loop-polling-for-changes.txt:235,搜「variable-length」)。 ↩ -
出处:「The event loop: polling for changes」第 558 段(
text/69-fm-the-event-loop-polling-for-changes.txt:558,搜「newFile」)。 ↩ -
出处:「The event loop: polling for changes」第 340 段(
text/69-fm-the-event-loop-polling-for-changes.txt:340,搜「remain open」)。 ↩↩↩ -
出处:「The event loop: polling for changes」第 312 段(
text/69-fm-the-event-loop-polling-for-changes.txt:312,搜「fflags」)。 ↩ -
出处:「The event loop: polling for changes」第 437 段(
text/69-fm-the-event-loop-polling-for-changes.txt:437,搜「two distinct purposes」)。 ↩ -
出处:「The event loop: polling for changes」第 520 段(
text/69-fm-the-event-loop-polling-for-changes.txt:520,搜「fd: 4」)。 ↩ -
出处:「The event loop: polling for changes」第 1101 段(
text/69-fm-the-event-loop-polling-for-changes.txt:1101,搜「snapshot」)。 ↩↩ -
出处:「The event loop: polling for changes」第 677 段(
text/69-fm-the-event-loop-polling-for-changes.txt:677,搜「base 8」)与第 707 段(text/69-fm-the-event-loop-polling-for-changes.txt:707,搜「avoids the need」)。 ↩ -
出处:「The event loop: polling for changes」第 928 段(
text/69-fm-the-event-loop-polling-for-changes.txt:928,搜「more sophisticated」)。 ↩ -
出处:「The event loop: polling for changes」第 987 段(
text/69-fm-the-event-loop-polling-for-changes.txt:987,搜「501」)。 ↩ -
出处:「The event loop: polling for changes」第 1025 段(
text/69-fm-the-event-loop-polling-for-changes.txt:1025,搜「longevity」)。 ↩ -
出处:「Moving a file」第 78 段(
text/64-fm-moving-a-file.txt:78,搜「atomic operation」)。 ↩ -
出处:「Moving a file」第 23 段(
text/64-fm-moving-a-file.txt:23,搜「EXDEV」)。 ↩ -
出处:「Deleting a file」第 48 段(
text/62-fm-deleting-a-file.txt:48,搜「decrements the inode link count」)。 ↩ -
出处:「Finding file metadata」第 135 段(
text/60-fm-finding-file-metadata.txt:135,搜「last accessed」)。 ↩ -
出处:「Finding file metadata」第 198 段(
text/60-fm-finding-file-metadata.txt:198,搜「not interchangeable」)。 ↩ -
出处:「Deleting a directory」第 88 段(
text/63-fm-deleting-a-directory.txt:88,搜「not a bug」)。 ↩ -
出处:「The event loop: polling for changes」第 163 段(
text/69-fm-the-event-loop-polling-for-changes.txt:163,搜「bitmask」)。 ↩ -
出处:「The event loop: polling for changes」第 312 段(
text/69-fm-the-event-loop-polling-for-changes.txt:312,搜「NOTE_WRITE」)。 ↩ -
出处:「The event loop: polling for changes」第 179 段(
text/69-fm-the-event-loop-polling-for-changes.txt:179,搜「msg_buf」)。 ↩