修改文件内的目录时kqueue不会触发

Noi*_*art 0 c macos bsd kqueue

我使用kquque来监控桌面:

  • flags - EV_ADD | EV_CLEAR
  • fflags - NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE
  • filter - EVFILT_VNODE

但是,当我.js使用sublime2软件在桌面上编辑文件时,它不会触发通知:(

请指教

这是我的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);
Run Code Online (Sandbox Code Playgroud)

aba*_*ert 5

我刚刚意识到你没有监视.js文件,你正在监视它的目录.这使得一切都变得不那么神秘了.

简短版本是:如果您打开文件并进行编写,则不会更改目录上的任何内容.如果原子保存文件,即更改目录,但崇高2不以原子默认保存.

所以,看目录中的任何改变的任何文件,你需要枚举所有目录中的文件并把它们都加kqueue,*以及目录.

观察目录将捕获原子保存(以及正在创建的新文件); 看着文件会抓住覆盖.(正在取消链接的文件将触发两者.)如果您担心性能......好吧,kqueue设计用于处理10000个文件描述符的切换,并且UFS和HFS +都不是同一目录中数十万个目录条目的良好文件系统,所以你可能没问题...但你可能想要添加一些警告或中止的代码,如果目录变得非常庞大.


如果您想了解为什么这是必要的,您必须考虑两种不同类型的保存是如何工作的.

一个write刚刚写入的文件描述符.该文件描述符可以在文件系统上有一个目录条目链接 - 但它可以很容易地没有(例如,它是在临时命名空间中创建的,或者您只是在创建文件后取消链接),或许多(例如,你)我创建了硬链接).所以它实际上不能更新"文件的目录条目",因为没有这样的东西.

另一方面,原子保存的工作原理是创建一个新的临时文件,写入该文件,然后rename将临时文件放在原始文件上.这rename必须更新目录,用指向新文件的条目替换指向旧文件的条目.(当然它也会发送DELETE文件本身的通知,因为文件正在丢失链接.而且你通常也会发送一个ATTRIB,因为大多数应用程序都希望新文件具有相同的扩展属性,额外的分支等.)


*这里有明显的竞争条件:如果在文件之间移动或删除readdir并将其添加到文件中kqueue,您将收到错误消息.您可能希望通过生成即时通知来处理该错误,或​​者您可能只是想忽略它 - 毕竟,从用户的角度来看,它与某人在您的程序启动和程序之间删除文件的情况没有太大区别你做的时间readdir.

  • 我认为声称"`kqueue`旨在处理成千上万的文件描述符的转换"是夸张的.进程可以打开的文件描述符数量有限制,可能不是*倍*成千上万.在OS X上,它是10240.此外,如果您正在观察整个文件层次结构,则需要在将文件添加到层次结构中以及从层次结构中删除文件时开始和停止观看,这非常有效. (2认同)
  • @Noitidart:如果您正在构建通用用途的东西,您可能需要查看现有的包装器之一,它在操作系统提供的任何内容之上为您提供了一个类似于“FSEvents”的接口,从 FreeBSD 到以最有效的方式将 Linux 预“inotify”到 Windows(包括使用守护进程和子进程来管理所有“kqueue” fd,而不是让每个客户端都这样做)。我不知道此类包装器的当前技术水平,但值得研究而不是重新发明轮子。 (2认同)