修改目录中的文件时不会触发目录上的 kqueue
kqueue on directory not trigger when file within is modified
我已经使用 kquque 监控桌面:
flags
- EV_ADD | EV_CLEAR
fflags
- NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE
filter
- EVFILT_VNODE
然而,当我在桌面上使用 sublime2 软件编辑 .js
文件时,它不会触发通知:(
请指教
这是我的 js-ctypes 代码:
var rez_fd = ostypes.API('kqueue')();
console.info('rez_fd:', rez_fd.toString(), uneval(rez_fd));
if (ctypes.errno != 0) {
throw new Error('Failed rez_fd, errno: ' + ctypes.errno);
}
this.kq = rez_fd;
this.path = OS.Constants.Path.desktopDir;
// Open a file descriptor for the file/directory that you want to monitor.
var event_fd = ostypes.API('open')(this.path, OS.Constants.libc.O_EVTONLY);
console.info('event_fd:', event_fd.toString(), uneval(event_fd));
if (ctypes.errno != 0) {
throw new Error('Failed event_fd, errno: ' + ctypes.errno);
}
// The address in user_data will be copied into a field in the event.If you are monitoring multiple files,you could,for example,pass in different data structure for each file.For this example,the path string is used.
var user_data = ctypes.cast(ctypes.char.array()(this.path), ctypes.void.ptr);
// Set the timeout to wake us every half second.
var timeout = ostypes.TYPE.timespec();
var useSec = 0;
var useNsec = 500000000;
timeout.tv_sec = useSec; // 0 seconds
timeout.tv_nsec = useNsec; // 500 milliseconds
// Set up a list of events to monitor.
var fflags = vnode_events = ostypes.CONST.NOTE_DELETE | ostypes.CONST.NOTE_WRITE | ostypes.CONST.NOTE_EXTEND | ostypes.CONST.NOTE_ATTRIB | ostypes.CONST.NOTE_LINK | ostypes.CONST.NOTE_RENAME | ostypes.CONST.NOTE_REVOKE; // ostypes.TYPE.unsigned_int
var events_to_monitor = ostypes.TYPE.kevent.array(ostypes.CONST.NUM_EVENT_FDS)();
var filter = ostypes.CONST.EVFILT_VNODE;
var flags = ostypes.CONST.EV_ADD | ostypes.CONST.EV_CLEAR;
EV_SET(events_to_monitor.addressOfElement(0), event_fd, filter, flags, fflags, 0, user_data);
// Handle events
var event_data = ostypes.TYPE.kevent.array(ostypes.CONST.NUM_EVENT_SLOTS)(); // 1 slot
var num_files = 1; // ostypes.TYPE.int
var continue_loop = 40; // Monitor for twenty seconds. // ostypes.TYPE.int
while (--continue_loop) {
var event_count = ostypes.API('kevent')(this.kq, ctypes.cast(events_to_monitor.address(), ostypes.TYPE.kevent.ptr), ostypes.CONST.NUM_EVENT_SLOTS, ctypes.cast(event_data.address(), ostypes.TYPE.kevent.ptr), num_files, timeout.address());
console.info('event_count:', event_count.toString(), uneval(event_count));
if (ctypes.errno != 0) {
throw new Error('Failed event_count, errno: ' + ctypes.errno + ' and event_count: ' + cutils.jscGetDeepest(event_count));
}
if (cutils.jscEqual(event_data.addressOfElement(0).contents.flags, ostypes.CONST.EV_ERROR)) {
throw new Error('Failed event_count, due to event_data.flags == EV_ERROR, errno: ' + ctypes.errno + ' and event_count: ' + cutils.jscGetDeepest(event_count));
}
if (!cutils.jscEqual(event_count, '0')) {
console.log('Event ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.ident) + ' occurred. Filter ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.filter) + ', flags ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.flags) + ', filter flags ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.fflags) + ', filter data ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.data) + ', path ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.udata /*.contents.readString()*/ ));
} else {
// No event
}
// Reset the timeout. In case of a signal interrruption, the values may change.
timeout.tv_sec = useSec; // 0 seconds
timeout.tv_nsec = useNsec; // 500 milliseconds
}
ostypes.API('close')(event_fd);
我刚刚意识到您没有监控 .js
文件,您正在监控它的目录。这让一切都变得不那么神秘了。
简短的版本是:如果你打开一个文件并写入它,那不会改变目录中的任何内容。如果您以原子方式保存文件,确实会更改目录,但 Sublime 2 默认情况下不会以原子方式保存。
因此,要监视目录中任何文件的任何更改,您需要枚举目录中的所有文件并将它们全部添加到 kqueue
,* 以及目录。
观察目录将捕获原子保存(和正在创建的新文件);观看文件将捕获覆盖。 (未linked 的文件会触发两者。)如果您担心性能......好吧,kqueue
旨在处理 10000 个文件描述符的切换,UFS 和 HFS+ 都不是一个好的文件系统同一目录中有数十万个目录条目,所以您可能没问题……但是如果目录变得非常庞大,您可能需要添加一些警告或中止的代码。
如果您想了解为什么这是必要的,您必须考虑这两种不同的保存方式。
A write
只是写入一个文件描述符。该文件描述符可以在文件系统上有一个目录条目 link——但它也可以很容易地有 none(例如,它是在一个临时命名空间中创建的,或者你只是取消 linked创建文件后的文件)或许多(例如,您已经为它创建了硬 links)。所以它实际上不能更新"the directory entry for the file",因为没有这样的东西。
另一方面,原子保存的工作原理是创建一个新的临时文件,写入该文件,然后 rename
-ing 临时文件覆盖原始文件。 rename
必须更新目录,将指向旧文件的条目替换为指向新文件的条目。 (当然,它还会为文件本身发送 DELETE
通知,因为文件正在丢失 link。而且您通常还会发送 ATTRIB
,因为大多数应用程序都需要新的文件具有相同的扩展属性、额外的分叉等)
* 这里有一个明显的竞争条件:如果在 readdir
之间移动或删除文件并将其添加到 kqueue
,您将得到一个错误.您可能希望通过生成即时通知来处理该错误,或者您可能只是想忽略它 — 毕竟,从用户的角度来看,这与有人在程序启动和运行之间删除文件的情况没有太大区别你做 readdir
.
的时间
我已经使用 kquque 监控桌面:
flags
-EV_ADD | EV_CLEAR
fflags
-NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE
filter
-EVFILT_VNODE
然而,当我在桌面上使用 sublime2 软件编辑 .js
文件时,它不会触发通知:(
请指教
这是我的 js-ctypes 代码:
var rez_fd = ostypes.API('kqueue')();
console.info('rez_fd:', rez_fd.toString(), uneval(rez_fd));
if (ctypes.errno != 0) {
throw new Error('Failed rez_fd, errno: ' + ctypes.errno);
}
this.kq = rez_fd;
this.path = OS.Constants.Path.desktopDir;
// Open a file descriptor for the file/directory that you want to monitor.
var event_fd = ostypes.API('open')(this.path, OS.Constants.libc.O_EVTONLY);
console.info('event_fd:', event_fd.toString(), uneval(event_fd));
if (ctypes.errno != 0) {
throw new Error('Failed event_fd, errno: ' + ctypes.errno);
}
// The address in user_data will be copied into a field in the event.If you are monitoring multiple files,you could,for example,pass in different data structure for each file.For this example,the path string is used.
var user_data = ctypes.cast(ctypes.char.array()(this.path), ctypes.void.ptr);
// Set the timeout to wake us every half second.
var timeout = ostypes.TYPE.timespec();
var useSec = 0;
var useNsec = 500000000;
timeout.tv_sec = useSec; // 0 seconds
timeout.tv_nsec = useNsec; // 500 milliseconds
// Set up a list of events to monitor.
var fflags = vnode_events = ostypes.CONST.NOTE_DELETE | ostypes.CONST.NOTE_WRITE | ostypes.CONST.NOTE_EXTEND | ostypes.CONST.NOTE_ATTRIB | ostypes.CONST.NOTE_LINK | ostypes.CONST.NOTE_RENAME | ostypes.CONST.NOTE_REVOKE; // ostypes.TYPE.unsigned_int
var events_to_monitor = ostypes.TYPE.kevent.array(ostypes.CONST.NUM_EVENT_FDS)();
var filter = ostypes.CONST.EVFILT_VNODE;
var flags = ostypes.CONST.EV_ADD | ostypes.CONST.EV_CLEAR;
EV_SET(events_to_monitor.addressOfElement(0), event_fd, filter, flags, fflags, 0, user_data);
// Handle events
var event_data = ostypes.TYPE.kevent.array(ostypes.CONST.NUM_EVENT_SLOTS)(); // 1 slot
var num_files = 1; // ostypes.TYPE.int
var continue_loop = 40; // Monitor for twenty seconds. // ostypes.TYPE.int
while (--continue_loop) {
var event_count = ostypes.API('kevent')(this.kq, ctypes.cast(events_to_monitor.address(), ostypes.TYPE.kevent.ptr), ostypes.CONST.NUM_EVENT_SLOTS, ctypes.cast(event_data.address(), ostypes.TYPE.kevent.ptr), num_files, timeout.address());
console.info('event_count:', event_count.toString(), uneval(event_count));
if (ctypes.errno != 0) {
throw new Error('Failed event_count, errno: ' + ctypes.errno + ' and event_count: ' + cutils.jscGetDeepest(event_count));
}
if (cutils.jscEqual(event_data.addressOfElement(0).contents.flags, ostypes.CONST.EV_ERROR)) {
throw new Error('Failed event_count, due to event_data.flags == EV_ERROR, errno: ' + ctypes.errno + ' and event_count: ' + cutils.jscGetDeepest(event_count));
}
if (!cutils.jscEqual(event_count, '0')) {
console.log('Event ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.ident) + ' occurred. Filter ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.filter) + ', flags ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.flags) + ', filter flags ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.fflags) + ', filter data ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.data) + ', path ' + cutils.jscGetDeepest(event_data.addressOfElement(0).contents.udata /*.contents.readString()*/ ));
} else {
// No event
}
// Reset the timeout. In case of a signal interrruption, the values may change.
timeout.tv_sec = useSec; // 0 seconds
timeout.tv_nsec = useNsec; // 500 milliseconds
}
ostypes.API('close')(event_fd);
我刚刚意识到您没有监控 .js
文件,您正在监控它的目录。这让一切都变得不那么神秘了。
简短的版本是:如果你打开一个文件并写入它,那不会改变目录中的任何内容。如果您以原子方式保存文件,确实会更改目录,但 Sublime 2 默认情况下不会以原子方式保存。
因此,要监视目录中任何文件的任何更改,您需要枚举目录中的所有文件并将它们全部添加到 kqueue
,* 以及目录。
观察目录将捕获原子保存(和正在创建的新文件);观看文件将捕获覆盖。 (未linked 的文件会触发两者。)如果您担心性能......好吧,kqueue
旨在处理 10000 个文件描述符的切换,UFS 和 HFS+ 都不是一个好的文件系统同一目录中有数十万个目录条目,所以您可能没问题……但是如果目录变得非常庞大,您可能需要添加一些警告或中止的代码。
如果您想了解为什么这是必要的,您必须考虑这两种不同的保存方式。
A write
只是写入一个文件描述符。该文件描述符可以在文件系统上有一个目录条目 link——但它也可以很容易地有 none(例如,它是在一个临时命名空间中创建的,或者你只是取消 linked创建文件后的文件)或许多(例如,您已经为它创建了硬 links)。所以它实际上不能更新"the directory entry for the file",因为没有这样的东西。
另一方面,原子保存的工作原理是创建一个新的临时文件,写入该文件,然后 rename
-ing 临时文件覆盖原始文件。 rename
必须更新目录,将指向旧文件的条目替换为指向新文件的条目。 (当然,它还会为文件本身发送 DELETE
通知,因为文件正在丢失 link。而且您通常还会发送 ATTRIB
,因为大多数应用程序都需要新的文件具有相同的扩展属性、额外的分叉等)
* 这里有一个明显的竞争条件:如果在 readdir
之间移动或删除文件并将其添加到 kqueue
,您将得到一个错误.您可能希望通过生成即时通知来处理该错误,或者您可能只是想忽略它 — 毕竟,从用户的角度来看,这与有人在程序启动和运行之间删除文件的情况没有太大区别你做 readdir
.