![为什么 inotify 事件会触发多次](https://linux22.com/image/878328/%E4%B8%BA%E4%BB%80%E4%B9%88%20inotify%20%E4%BA%8B%E4%BB%B6%E4%BC%9A%E8%A7%A6%E5%8F%91%E5%A4%9A%E6%AC%A1.png)
这个问题源于我之前在堆栈溢出。 我在用观察者- 同样的问题也适用于英科龙- 监视文件夹及其子文件夹的变化,并将这些变化悄悄地保存到 Dropbox。
我监控write_close
事件 - IN_CLOSE_WRITE
- 出于这个目的。最初我监控的是modify
事件,即 IN_MODIFY。虽然这有效,但我发现在写入大文件时它会触发多次。这听起来很公平,所以我改用了,IN_CLOSE_WRITE
因为我觉得假设对于给定文件它只会发生一次是相当公平的。
但事实并非如此。即使是在 Nano 中创建的非常小的文本文件(只有一个字符),该事件也会发生两次。当同一个文件在 Dropbox 上同步两次时,这最多会导致不必要的流量。就我自己而言,这会导致灾难,因为在第一次事件中我执行了同步,然后删除了服务器端文件。结果 - 在第二次事件中,Dropbox 端文件变成了 0 字节文件。
我现在处理这个问题的方法是先让我的同步脚本休眠 10 秒,然后再执行其他操作,然后检查相关文件是否仍然存在,然后再尝试 Dropbox 同步。这种方法有效,因为在第二次迭代时文件丢失,脚本就会终止。
这听起来最多只能算是黑客行为。也许这不是一个糟糕的黑客行为,但我更愿意理解——为什么这个IN_CLOSE_WRITE
事件会发生不止一次?
一些额外的信息
- 检查以确保没有多个观察程序实例正在运行。
输出自ps ax|grep watcher.py
23880 ? Sl 0:01 python /usr/local/bin/watcher.py restart
24977 pts/0 S+ 0:00 grep --color=auto watcher.py
文件系统是ext4
。我应该提到,我在 Incron 中遇到了完全相同的问题。我从通过执行的批处理脚本启动 Watcher 守护程序/etc/rc2.d
。Incron OTH 通过其默认安装启动,无需我进行任何操作apt-get install incron
。
我的文件的本质watcher.ini
如下所示。
[DEFAULT]
logfile=/var/log/watcher.log
pidfile=/var/run/watcher.pid
[job1]
watch=/path/to/watch
events=write_close
excluded=
recursive=true
autoadd=true
command=/home/datastore.php $filename
我将datastore.php
脚本精简到最基本的要素,以验证它是否在没有任何混乱的 Dropbox 上传 + 源删除代码的情况下启动两次。
#! /usr/bin/php
<?php
file_put_contents('/tmp/watcher',$argv[1],FILE_APPEND);
?>
然后,我在有问题的路径上创建了一个小文件,然后进行了检查/tmp/watcher
。问题仍然存在 - 该文件仍然有两个连续的条目$argv[1]
。
答案1
我刚刚使用 IN_CLOSE_WRITE 遇到了这个问题,最后找到了问题所在。我原来的脚本是从 stdin 逐行读取邮件,然后将每一行写入文件。这导致 incron 在每次写入时触发。我根据 incron 事件的数量和文件中的行数看到了这一点。
然后,我修改了原始脚本,在写入文件之前读取所有 stdin。一旦这样做了,我就不再看到对同一个文件执行多个 incron 了。
答案2
我没有足够的声望来将此作为评论发布,但你确定没有创建临时的、可能隐藏的文件吗?我遇到了inotifywait
多次触发的类似问题,但我意识到这是因为 vim 在编辑时会创建一个 .swp 文件,这会在关闭时触发事件。它还会从原始文件中获取关闭事件。
听起来你注意到了该事件在同一个文件上触发了多个文件,这不是我能重现的——这只会在临时文件中发生一次,在原始文件中发生一次。
我尝试使用 nano 进行了快速测试,我认为它根本不会创建临时文件(至少对于少数字符的情况),但是您的设置中是否还有其他东西可以依赖于类似的行为?
答案3
我不确定,但很可能第一个 write_close 将文件属性(如创建时间)写入其中,然后才写入实际数据。实际上,rsync 会创建一个临时文件,当所有操作完成后,它会将临时文件移动到同一文件夹中的实际文件,因此在使用 rsync 时很容易监控是否正常创建,而移动是一个原子操作。另一方面,在 inotify 中有一个叫做一次性的东西,可能使用它我们可以在第一个修改消息上触发某些操作,并且如您所建议的那样,在开始操作之前休眠合理的时间。我现在正在研究这个问题,一旦发现任何新东西,我就会更新。 https://superuser.com/questions/1133642/traceing-the-moment-when-file-is-completely-copied-to-samba-share-with-inotify
答案4
我注意到如果我使用>
或,我会得到 2 个事件cp
:
root@vm-dvde05:~# touch file
root@vm-dvde05:~# inotifywait --recursive --monitor --event modify file &
[1] 31835
root@vm-dvde05:~# Setting up watches. Beware: since -r was given, this may take a while!
Watches established.
root@vm-dvde05:~# echo string>file
file MODIFY
file MODIFY
root@vm-dvde05:~# cat file
string
root@vm-dvde05:~# cp /etc/hosts file
file MODIFY
file MODIFY
root@vm-dvde05:~#
但如果我使用附加>>
它会显示单个事件:
root@vm-dvde05:~# echo string2>>file
file MODIFY
root@vm-dvde05:~# kill %1
root@vm-dvde05:~#
[1]+ Terminated inotifywait --recursive --monitor --event modify file
root@vm-dvde05:~#
为了获取我监视的单个事件CLOSE_WRITE