附1 FSEvents 高级 API(异步的事件驱动 API)
这一节的内容主要是在汇总苹果的官方文档.
如下仍然将高级 API 记为 FSEvents 便于识别.
概述
文件系统事件(FSEvents) API 可以向应用程序提供针对目录中任何修改的通知, 比如应用可以通过它快速检测用户对某个文件的修改. 并且它还提供了一个轻量化的途径来使应用下次启动时, 可以获取到这之间目录中发生的任何变化, 比如备份软件就可以通过这样的方式来确定某个文件是否在给定的时间戳或事件 ID 的前提下是否有修改.
本节主要有如下四个部分的内容:
- 总览: 文件系统事件 API 总览, 以及 API 的工作原理
- 如何使用文件系统事件 API: 讲如何 使用这个 API, 包含如何创建 event stream, 编写 handler 等.
- 文件系统事件 API 的安全: 讲 API 相关的安全功能
- Kernel Queues: 它是另外一种机制, 可以实现类似文件系统事件 API 的功能. 会讲它的工作原理以及在什么情况下用它会更合适.
API 总览
FSEvents API 在 10.5 及之后的系统都可用, 应用可以使用它监听目录中任何改变并作出响应.
FSEvents 从机制上讲, 有如下三个部分组成(详见FSEvents 底层 API 的内容):
- 内核代码部分: 将"原始事件通知"通过一个特殊设备(
/dev/fsevents
)从内核空间传递到用户空间 - 守护进程
fseventsd
: 过滤并发送这些事件 - 数据库: 记录所有的改变事件.
当应用注册了 FSEvents 通知后, 守护进程会在对应目录中(或目录本身)的任何改变发生后发送一个通知给应用.
需要注意: 守护进程发送的通知粒度是在目录层级的. 它只会告诉应用目录中有什么改变, 但不会详细告诉应用改变的具体内容是什么(否则通知就太臃肿了).
除了将多个文件的改变情况都压缩到一个通知外, 如果多个改变在很短的时间内发生, 守护进程还会将这些改变通知聚合到一起. 意味着在某段时间内目录中的最后一个改变发生后, 守护进程可以保证应用总是能够收到至少一个通知. 注册通知时应用可以指定自己想要的最小时间粒度(时间粒度内的改变都会放到一个通知).
高层的 FSEvents API 的限制主要有:
- 它并不是"实时"的, 文件系统的改变不能在发生后就被立即送达, 因此杀毒软件才 会直接编写内核扩展并在 VFS 的层级上监听改变.
- 它不能反映某个特定文件改变的精确时间点(因为通知是延迟发送的), 如果有改变时间上的需要, 可以使用 kqueue(后面会讲).
高层 FSEvents API 主要用于被动监听一个大型文件树中的改变, 它非常适合备份软件, 实际上苹果自己的备份就是基于高层 FSEvents API 的. 另外它也非常适合一些将自己的数据文件分散存储到磁盘中的应用, 当其他应用改变了某个关联的数据文件时, 该应用就可以收到通知并对应处理.
虽然这些也可以通过 kqueue 实现, 但 FSEvents API 提供了数据连续性的保证(应用下次启动后仍然可以获取退出期间的所有改变).
高层 FSEvents API 的用法
FSEvents API 由若干组函数构成, 比如可以使用 FSEvents
开头的一些函数获取卷/事件的总体信息, 也可以使用 FSEventStream
开头的函数来创建事件流/操作事件流.
FSEvents 事件流是 API 的核心概念, 下面先来看它的生命期.
FSEvents 事件流的生命期:
- 应用通过
FSEventStreamCreate
或FSEventStreamCreateRelativeToDevice
创建事件流 - 调用
FSEventStreamScheduleWithRunLoop
将事件流调度到指定的运行循环上执行. - 调用
FSEventStreamStart
, 告诉fseventsd
守护进程可以开始发送通知过来了 - 应用自己处理接收到的事件(FSEvents 想应用提供事件的方式是调用在第 1 步中设置的回调)
- 应用在合适的时机调用
FSEventStreamStop
, 告诉守护进程停止发送通知