缺少 inotify 事件(在 .git 目录中)

Missing inotify events (in .git directory)

我正在使用 inotify 事件监视文件的更改(碰巧,从 Python 调用 libc)。

对于 git clone 期间的一些文件,我看到了一些奇怪的东西:我看到了一个 IN_CREATE 事件,并且我通过 ls 看到文件有内容,但是,我从来没有参见 IN_MODIFYIN_CLOSE_WRITE。这引起了我的问题,因为我想对文件做出回应 IN_CLOSE_WRITE:具体而言,启动文件内容的上传。

行为异常的文件在 .git/objects/pack 目录中,它们以 .pack.idx 结尾。 git 创建的其他文件具有更规则的 IN_CREATE -> IN_MODIFY -> IN_CLOSE_WRITE 链(我没有关注 IN_OPEN 事件)。

这是在 MacOS 的 docker 中,但我在远程系统的 docker 和 Linux 中看到了相同的证据,所以我怀疑 MacOS 方面不是相关的。如果 watching 和 git clonesame docker 容器中,我会看到这个。

我的问题:


编辑:阅读 https://developer.ibm.com/tutorials/l-inotify/ 看起来我所看到的与

一致

我认为我的问题是尝试使用 inotify 作为上传文件的触发器,然后减少到注意到 .pack 文件很难 link 到另一个文件,然后上传这种情况?

我可以推测 Git 大部分时间使用 atomic 文件更新,它是这样完成的:

  1. 文件内容被读入内存(并修改)。
  2. 修改后的内容被写入一个单独的文件(通常位于与原始文件相同的目录中,并具有随机(mktemp 样式)名称。
  3. 然后新文件 rename(2)d -d 覆盖原来的文件;此操作保证每个尝试使用其名称打开文件的观察者都将获得旧内容或新内容。

此类更新被 inotify(7) 视为 moved_to 事件——因为目录中的文件 "reappears"。

基于 this accepted answer 我假设事件可能会因使用的协议(即 ssh 或 https)而有所不同。

使用 --no-hardlinks 选项监视从本地文件系统克隆时是否观察到相同的行为?

$ git clone git@github.com:user/repo.git
# set up watcher for new dir
$ git clone --no-hardlinks repo new-repo

您在 运行 上观察到的行为 linux 和 Mac 主机上的实验可能消除了这个未解决的问题,因为 https://github.com/docker/for-mac/issues/896 但添加只是为了以防万一。

在 Linux 4.19.95 上单独回答 git 2.24.1 的问题:

  • Why are these events missing on these files?

您看不到 IN_MODIFY/IN_CLOSE_WRITE 事件,因为 git clone 将始终尝试对 .git/objects 目录下的文件使用硬链接。通过网络或跨文件系统边界进行克隆时,这些事件将再次出现。

  • What can be done about it? Specifically, how can I respond to the completion of writes to these files? Note: ideally I would like to respond when writing is "finished" to avoid needlessly/(incorrectly) uploading "unfinished" writing.

为了捕获对硬链接的修改,您必须为 inotify CREATE 事件设置一个处理程序,它会跟随并跟踪这些链接。请注意,一个简单的 CREATE 也可能意味着创建了一个非空文件。然后,对于任何文件的 IN_MODIFY/IN_CLOSE_WRITE,您也必须对所有链接的文件触发相同的操作。显然,您还必须在 DELETE 事件中删除该关系。

一种更简单、更可靠的方法可能是定期散列所有文件并检查文件内容是否已更改。


更正

仔细检查 git 源代码并 运行 gitstrace 后,我发现 git 确实使用了内存映射文件,但主要是用于阅读内容。 See the usage of xmmap which is always called with PROT_READ only.。因此,我之前在下面的回答是 NOT 正确答案。尽管如此,出于信息目的,我仍然想将其保留在这里:

  • 您没有看到 IN_MODIFY 事件,因为 packfile.c 使用 mmap 进行文件访问并且 inotify 不报告 [=28] 的修改=]编辑文件。

    来自inotify manpage:

    The inotify API does not report file accesses and modifications that may occur because of mmap(2), msync(2), and munmap(2).

还有一种可能(来自man inotify):

Note that the event queue can overflow. In this case, events are lost. Robust applications should handle the possibility of lost events gracefully. For example, it may be necessary to rebuild part or all of the application cache. (One simple, but possibly expensive, approach is to close the inotify file descriptor, empty the cache, create a new inotify file descriptor, and then re-create watches and cache entries for the objects to be monitored.)

虽然 git clone 会产生繁重的事件流,但这种情况是有可能发生的。

如何避免这种情况:

  1. 增加读取缓冲区,试试fcntl(F_SETPIPE_SZ)(这个方法是猜测,我没试过)
  2. 在专用线程中将事件读入大缓冲区,在另一个线程中处理事件。

也许你犯了我多年前犯的同样的错误。我只使用过 inotify 两次。第一次,我的代码简单地工作了。后来没有那个源了,又开始了,但是这次,不知道为什么,少了一些事件。

原来我在读一个事件的时候,真的是在读一小批事件。我分析了我期望的那个,以为就是这样,就是这样。最终,我发现接收到的数据还有更多,当我添加一些代码来解析从一次读取中接收到的所有事件时,没有更多的事件丢失。