编程语言研究书库

第 5 章拆解 — 目录遍历、文件事件与权限

目录遍历、文件事件与权限

这一章讲什么: 把「文件系统」从一层文件名皮扒到内核对象:每个文件真正的身份是 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:

.
├── LICENSE
├── README.md
├── ch01
…
└── ch12

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),与文件大小无关;目标已存在也在单次事务里被替换,任何其他进程不会看到「搬到一半」的状态32。跨分区则系统调用直接失败(EXDEV,无效跨设备链接),应用必须退化成「手动拷数据 + 删原件」——这也是为什么大文件搬家跨盘时慢,而同盘改个名字永远瞬时的全部原因33。

deleteFile() 底下是 unlink(2),这个名字比「删除」诚实:它不一定擦数据——它摘掉文件名这个目录项,把 inode 的链接计数减一;计数归零且没有任何进程还开着这个文件,内核才把磁盘块标为可复用34。文件被删了却还在被进程持有的场景(日志切割、运行中的二进制自更新)全靠这个语义兜底——「删了就消失」是表象,「引用计数归零才真正回收」才是机制。

4.3 时间戳与权限位怎么读

file.stat() 一调带回全部:大小、权限、三个时间戳。atime 是最后访问,mtime 是最后改内容,ctime 是最后改元数据——备份系统拿 mtime/ctime 判断增量,审计工具拿 ctime 发现「内容没变但权限被动过」35。打印用 {o} 格式化输出八进制,这是 UNIX 权限的传统写法;0o40755 里前缀 40 是文件类型位(目录),755 才是权限,程序里读写模式值时两者不可互换36。

4.4 错误的人格:DirNotEmpty 与调试期栈

删非空目录,rmdir(2) 直接报 error.DirNotEmpty,Debug 模式下错误沿调用栈传播回 main,运行时打印一行回溯——这不是 bug,是 Debug 构建的预期行为;发布版构建里回溯被抑制,只剩应用自己的一行话。生产工具的体面做法是在顶层接住错误、打印人话、std.process.exit(1),而不是把原始错误抛给终端37。

4.5 zwatch 的两个 registration 细节

inotify 注册用的是位掩码的"或":IN.MODIFY | IN.CREATE | IN.DELETE——位掩码精确告诉内核对 inode 的哪些状态变化感兴趣,单个队列 fd multiplex 全部目录,内核替应用扛住跨文件系统的跟踪重活38。kqueue 的注册多一步手工:kevent 结构里 ident 填目录 fd、filter 填 EVFILT_VNODE(文件系统级)、flags 填 EV_ADD | EV_ENABLE | EV_CLEAR(注册、启用、触发后自动清除),fflags 填 NOTE_WRITE | NOTE_DELETE | NOTE_RENAME(写、删、改名)39。EV_CLEAR 的"触发即自清"意味着每次事件都要重新武装——换来的是不会有重复通知堆积。

消息缓冲也有一个低耗姿势:zwatch 用一块 512 字节的栈上 msg_buf 配合 bufPrint 反复格式化状态行——不申请新堆内存,监控循环的 setup 阶段也保持轻量40。

5. 作者的判断与证据

  • 判断: 自包含的小程序是学系统编程的正道——「逻辑、数据结构、系统调用全在一个可读的源文件里,没有隐藏配置、没有不透明依赖」,而且这样的程序多年以后更可能还编译得过,哪怕语言本身又有破坏性变更41。这是全书的方法论宣言,也解释了为什么每章都是一堆独立小工具而不是一个大框架。
  • 判断: 事件驱动优于轮询,因为闲时代价归零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. 可带走的

  1. 文件名是皮,inode 是里;改权限/属主 = 改 inode 字段,不碰数据。
  2. 权限记三根轴(主/组/他)× 三位(4/2/1);注意 0o40755 里 40 是文件类型、755 才是权限。
  3. symlink 是文件不是别名;区分链接和目标必须走 lstat(follow_symlinks = false)。
  4. openDir 不带 .iterate = true 就没法迭代——这个开关坑过不止一个人。
  5. 规范化路径:热路径用 realPathFile 栈缓冲,通用代码用 Alloc 版;三条路跑同输入可做交叉验证。
  6. 递归画树三件套:收集排序保稳定、前缀累积保对齐、符号链接只看不进防死环。
  7. builtin.os.tag 是编译期常量,不命中分支不进二进制;else 里 @compileError 当平台守卫。
  8. 事件驱动 vs 轮询:闲时代价归零,阻塞读就是全部等待逻辑。
  9. inotify:一个队列 fd multiplex 所有目录,事件是「定长头+变长名」,指针算术逐条剥。
  10. kqueue:看的是 fd 不是路径,每个被看对象一个 fd 且必须保活;给不出文件名,想要就自己快照对比。
  11. std.c 直接暴露了一批 POSIX 函数(fchmod 等),不是每次都要 @cImport。
  12. 数字 UID/GID 是内核语言;ls 显示的 mtsouk:staff 是查库翻译。

7. 原文地图

主题原书章原文位置
inode 与权限位Understanding inodes, file permissions, and symlinkstext/56-fm-understanding-inodes-file-permissions-and-symlin.txt:11(搜「index node」)
vnode 与 VFSUnderstanding inodes, file permissions, and symlinkstext/56-fm-understanding-inodes-file-permissions-and-symlin.txt:35(搜「virtual node」)
三轴权限模型Understanding inodes, file permissions, and symlinkstext/56-fm-understanding-inodes-file-permissions-and-symlin.txt:63(搜「three distinct categories」)
symlink vs 硬链接、lstatUnderstanding inodes, file permissions, and symlinkstext/56-fm-understanding-inodes-file-permissions-and-symlin.txt:143(搜「simply another name」)
规范化与三条解析路Resolve a relative path to its absolute counterparttext/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:9(搜「canonicalization」)
栈缓冲 vs Alloc 的选择Resolve a relative path to its absolute counterparttext/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:130(搜「fixed-buffer approach」)
iterate = true 开关Listing directory entriestext/58-fm-listing-directory-entries.txt:75(搜「prepare the directory」)
follow_symlinks = falseFinding the kind of filetext/59-fm-finding-the-kind-of-file.txt:81(搜「important here」)
元数据一调全取Finding file metadatatext/60-fm-finding-file-metadata.txt:29(搜「the tool is defined」)
0o40755 的类型前缀Finding file metadatatext/60-fm-finding-file-metadata.txt:174(搜「0o40755」)
rename 同分区 O(1)Moving a filetext/64-fm-moving-a-file.txt:9(搜「same physical partition」)
ztree 递归签名Recursively walking directoriestext/65-fm-recursively-walking-directories.txt:29(搜「printTree」)
收集排序保稳定Recursively walking directoriestext/65-fm-recursively-walking-directories.txt:141(搜「deterministic」)
符号链接不深入Recursively walking directoriestext/65-fm-recursively-walking-directories.txt:191(搜「deliberate safety decision」)
ztree 实测输出Developing the utilitytext/66-fm-developing-the-utility.txt:219(搜「LICENSE」)
编译期平台分支Finding out the active operating systemtext/67-fm-finding-out-the-active-operating-system.txt:48(搜「compile-time constant」)
事件队列模型Understanding file eventstext/68-fm-understanding-file-events.txt:13(搜「queue」)
inotify 注册两步The event loop: polling for changestext/69-fm-the-event-loop-polling-for-changes.txt:141(搜「centralized queue」)
变长事件指针算术The event loop: polling for changestext/69-fm-the-event-loop-polling-for-changes.txt:235(搜「variable-length」)
kqueue 的 fd 保活负担The event loop: polling for changestext/69-fm-the-event-loop-polling-for-changes.txt:340(搜「remain open」)
kevent 两用The event loop: polling for changestext/69-fm-the-event-loop-polling-for-changes.txt:437(搜「two distinct purposes」)
两平台输出对照The event loop: polling for changestext/69-fm-the-event-loop-polling-for-changes.txt:520(搜「fd: 4」) · :558(搜「newFile」)
kqueue 文件名练习The event loop: polling for changestext/69-fm-the-event-loop-polling-for-changes.txt:1101(搜「snapshot」)
iChmod 八进制解析The event loop: polling for changestext/69-fm-the-event-loop-polling-for-changes.txt:677(搜「base 8」)
zchown 数字 ID 与 ls 翻译The event loop: polling for changestext/69-fm-the-event-loop-polling-for-changes.txt:987(搜「501」)
「自包含小程序」方法论The event loop: polling for changestext/69-fm-the-event-loop-polling-for-changes.txt:1025(搜「longevity」)

本章拆解依据原书第 5 章(含各小节)。每条出处可用 node scripts/book-verify.mjs systems-programming-with-zig 回核。

Footnotes

  1. 出处:「Understanding inodes, file permissions, and symlinks」第 11 段(text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:11,搜「index node」)。 ↩

  2. 出处:「Understanding inodes, file permissions, and symlinks」第 35 段(text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:35,搜「virtual node」)。 ↩

  3. 出处:「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」)。 ↩

  4. 出处:「Finding file metadata」第 174 段(text/60-fm-finding-file-metadata.txt:174,搜「0o40755」)。 ↩ ↩2

  5. 出处:「Understanding inodes, file permissions, and symlinks」第 143 段(text/56-fm-understanding-inodes-file-permissions-and-symlin.txt:143,搜「simply another name」)。 ↩

  6. 出处:「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」)。 ↩

  7. 出处:「Resolve a relative path to its absolute counterpart」第 173 段(text/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:173,搜「foundational operation」)。 ↩

  8. 出处:「Resolve a relative path to its absolute counterpart」第 9 段(text/57-fm-resolve-a-relative-path-to-its-absolute-counterp.txt:9,搜「canonicalization」)。 ↩

  9. 出处:「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」)。 ↩

  10. 出处:「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」)。 ↩

  11. 出处:「Listing directory entries」第 75 段(text/58-fm-listing-directory-entries.txt:75,搜「prepare the directory」)。 ↩

  12. 出处:「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」)。 ↩

  13. 出处:「Finding file metadata」第 29 段(text/60-fm-finding-file-metadata.txt:29,搜「the tool is defined」)。 ↩

  14. 出处:「Recursively walking directories」第 29 段(text/65-fm-recursively-walking-directories.txt:29,搜「printTree」)。 ↩

  15. 出处:「Recursively walking directories」第 141 段(text/65-fm-recursively-walking-directories.txt:141,搜「deterministic」)。 ↩

  16. 出处:「Recursively walking directories」第 155 段(text/65-fm-recursively-walking-directories.txt:155,搜「new」)。 ↩

  17. 出处:「Recursively walking directories」第 191 段(text/65-fm-recursively-walking-directories.txt:191,搜「deliberate safety decision」)。 ↩ ↩2

  18. 出处:「Developing the utility」第 219 段(text/66-fm-developing-the-utility.txt:219,搜「LICENSE」)。 ↩

  19. 出处:「Finding out the active operating system」第 48 段(text/67-fm-finding-out-the-active-operating-system.txt:48,搜「compile-time constant」)。 ↩ ↩2

  20. 出处:「Understanding file events」第 17 段(text/68-fm-understanding-file-events.txt:17,搜「zero CPU cycles」)。 ↩ ↩2

  21. 出处:「The event loop: polling for changes」第 141 段(text/69-fm-the-event-loop-polling-for-changes.txt:141,搜「centralized queue」)。 ↩

  22. 出处:「The event loop: polling for changes」第 235 段(text/69-fm-the-event-loop-polling-for-changes.txt:235,搜「variable-length」)。 ↩

  23. 出处:「The event loop: polling for changes」第 558 段(text/69-fm-the-event-loop-polling-for-changes.txt:558,搜「newFile」)。 ↩

  24. 出处:「The event loop: polling for changes」第 340 段(text/69-fm-the-event-loop-polling-for-changes.txt:340,搜「remain open」)。 ↩ ↩2 ↩3

  25. 出处:「The event loop: polling for changes」第 312 段(text/69-fm-the-event-loop-polling-for-changes.txt:312,搜「fflags」)。 ↩

  26. 出处:「The event loop: polling for changes」第 437 段(text/69-fm-the-event-loop-polling-for-changes.txt:437,搜「two distinct purposes」)。 ↩

  27. 出处:「The event loop: polling for changes」第 520 段(text/69-fm-the-event-loop-polling-for-changes.txt:520,搜「fd: 4」)。 ↩

  28. 出处:「The event loop: polling for changes」第 1101 段(text/69-fm-the-event-loop-polling-for-changes.txt:1101,搜「snapshot」)。 ↩ ↩2

  29. 出处:「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」)。 ↩

  30. 出处:「The event loop: polling for changes」第 928 段(text/69-fm-the-event-loop-polling-for-changes.txt:928,搜「more sophisticated」)。 ↩

  31. 出处:「The event loop: polling for changes」第 987 段(text/69-fm-the-event-loop-polling-for-changes.txt:987,搜「501」)。 ↩

  32. 出处:「Moving a file」第 78 段(text/64-fm-moving-a-file.txt:78,搜「atomic operation」)。 ↩

  33. 出处:「Moving a file」第 23 段(text/64-fm-moving-a-file.txt:23,搜「EXDEV」)。 ↩

  34. 出处:「Deleting a file」第 48 段(text/62-fm-deleting-a-file.txt:48,搜「decrements the inode link count」)。 ↩

  35. 出处:「Finding file metadata」第 135 段(text/60-fm-finding-file-metadata.txt:135,搜「last accessed」)。 ↩

  36. 出处:「Finding file metadata」第 198 段(text/60-fm-finding-file-metadata.txt:198,搜「not interchangeable」)。 ↩

  37. 出处:「Deleting a directory」第 88 段(text/63-fm-deleting-a-directory.txt:88,搜「not a bug」)。 ↩

  38. 出处:「The event loop: polling for changes」第 163 段(text/69-fm-the-event-loop-polling-for-changes.txt:163,搜「bitmask」)。 ↩

  39. 出处:「The event loop: polling for changes」第 312 段(text/69-fm-the-event-loop-polling-for-changes.txt:312,搜「NOTE_WRITE」)。 ↩

  40. 出处:「The event loop: polling for changes」第 179 段(text/69-fm-the-event-loop-polling-for-changes.txt:179,搜「msg_buf」)。 ↩

  41. 出处:「The event loop: polling for changes」第 1025 段(text/69-fm-the-event-loop-polling-for-changes.txt:1025,搜「longevity」)。 ↩

On this page