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);

person Noitidart    schedule 02.05.2015    source источник
comment
Что ж, это должно работать; возможно, вы сделали что-то не так в коде, который нам не показали. Так покажите нам код.   -  person abarnert    schedule 02.05.2015
comment
Спасибо @abarnert, я посмотрю на это. Мой код — js-ctypes, поэтому я не решаюсь им поделиться, так как это сбивает людей с толку, ха-ха.   -  person Noitidart    schedule 02.05.2015
comment
Да, у меня были похожие проблемы с использованием библиотек моста/FFI; сначала вы выясняете, как писать свой код из документации и примеров кода, написанного для C (или C++, или ObjC, или чего-то еще), а затем, когда у вас возникает проблема, вы должны перевести все это обратно, прежде чем соответствующий эксперт сможет понять это. Если вы можете создать MCVE на C, это поможет; если вы не можете, просто опубликуйте его в JS.   -  person abarnert    schedule 02.05.2015
comment
Спасибо @abarnert! Постараюсь написать что-нибудь понятное :)   -  person Noitidart    schedule 02.05.2015
comment
Я добавил код @abarnert :) Он в js-ctypes, все работает нормально, это так странно, что он не запускает изменение содержимого в файле .js, я получаю изменение содержимого в файле .DSStroe   -  person Noitidart    schedule 02.05.2015
comment
Нет, неважно… Я хотел подчеркнуть, что большинство редакторов на самом деле не перезаписывают ваш файл, вместо этого они записывают временный файл, а затем rename поверх оригинала, так что вы не получите файл с измененным содержимым. уведомление. Но вы все равно должны получить уведомление об отмене связи (NOTE_DELETE, которое вы, похоже, не пропустили), и вы его тоже не получаете.   -  person abarnert    schedule 02.05.2015
comment
Ах, спасибо за эту информацию, в отличие от того, что я этого не знал, я продолжу отлаживать это :)   -  person Noitidart    schedule 02.05.2015
comment
Возможно, вы захотите попробовать сделать аналогичные вещи за пределами Sublime, например, cp myfile tmpfile; mv myfile thefile и убедиться, что это запускает NOTE_DELETE на myfile.   -  person abarnert    schedule 02.05.2015
comment
Что записывается в консоль (и в консоль JavaScript, и в журнал системной консоли)? В каком контексте это работает (например, в браузере)? Может ли быть проблема с песочницей?   -  person Ken Thomases    schedule 02.05.2015
comment
И последнее: согласно этой ветке, Sublime на самом деле имеет возможность выбрать атомарное сохранение или перезапись, и по умолчанию она может быть настроена на перезапись, поэтому моя последняя пара комментариев может быть неактуальна.   -  person abarnert    schedule 02.05.2015
comment
Спасибо @abarnert, это то, что я надеялся поймать, возвышенное делает что-то другое, файл модифицируется, и все другие ОС перехватывают это событие. Например, FSEvents его ловит, но для OSX ‹ 10.7 мне приходится использовать kqueue, а для *bsd. Поэтому я пытаюсь обнаружить атомарное сохранение и перезапись.   -  person Noitidart    schedule 02.05.2015
comment
Спасибо @KenThomases, это определенно работает, просто оно не срабатывает для определенных событий, таких как этот возвышенный случай, хотя содержимое файла действительно меняется, это очень странно. Я делаю API для использования в надстройках Firefox для систем OSX и BSD. (OSX 10.7+ я использую FSEvents).   -  person Noitidart    schedule 02.05.2015
comment
Если вы отслеживаете каталог, а Sublime не выполняет атомарное сохранение, вам не следует ожидать, что каталог изменится. Sublime открывает файл и записывает в него. Это не меняет каталог, содержащий файл.   -  person Ken Thomases    schedule 02.05.2015
comment
Спасибо @KenThomases, но когда такие вещи, как .DSStore, изменяются, я получаю триггер для каталога, затем я определяю каталог и сравниваю lastModTimes. Но я не получаю триггер с возвышенным для перезаписи / атомарного сохранения :( Я просто пытаюсь заставить его запускать уведомление в каталоге, поскольку статистика заботится об идентификации того, что произошло.   -  person Noitidart    schedule 02.05.2015
comment
Это означает, что .DS_Store изменяется не так, как Sublime изменяет файлы. Посмотрите на номера инодов. Каталог изменяется только при переименовании файла, удалении связи с существующим файлом или создании нового файла. Одна или несколько из этих вещей происходят при атомарном сохранении, но не при простом открытии и изменении файла. Изменение времени последнего изменения файла не является изменением каталога. Это изменение файла. Вам, вероятно, следует изучить использование API FSEvents, а не kqueue, чтобы отслеживать это.   -  person Ken Thomases    schedule 02.05.2015
comment
Спасибо @KenThomases за это прекрасное объяснение. Я использовал FSEvents для OSX 10.7+, но для системы BSD kqueue - мой единственный вариант :(   -  person Noitidart    schedule 02.05.2015


Ответы (1)


Я только что понял, что вы следите не за файлом .js, вы следите за его каталогом. Это делает все намного менее загадочным.

Краткая версия: если вы открываете файл и записываете его, это ничего не меняет в каталоге. Если вы автоматически сохраняете файл, это действительно меняет каталог, но Sublime 2 не сохраняет атомарно по умолчанию.

Итак, чтобы отслеживать любые изменения в любом файле в каталоге, вам нужно перечислить все файлы в каталоге и добавить их все в kqueue,*, а также в каталог.

Наблюдение за каталогом позволит поймать атомарные сохранения (и новые создаваемые файлы); просмотр файлов поймает перезапись. (Файлы, которые не связаны, вызывают и то, и другое.) Если вы беспокоитесь о производительности… ну, kqueue предназначен для переключения на 10000 файловых дескрипторов, и ни UFS, ни HFS+ не являются хорошей файловой системой для сотен тысяч записей каталога в одном каталоге. , так что вы, вероятно, в порядке ... но вы можете добавить код, который предупреждает или прерывает работу, если каталог оказывается очень большим.


Если вы хотите понять, почему это необходимо, вы должны подумать о том, как работают два разных типа сохранений.

write просто пишет в дескриптор файла. Этот файловый дескриптор может иметь одну ссылку на вход в каталог в файловой системе, но с таким же успехом может не иметь ни одной (например, он был создан во временном пространстве имен или вы просто разорвали связь с файлом после его создания) или многих (например, вы мы создали жесткие ссылки на него). Таким образом, он не может фактически обновить «запись каталога для файла», потому что такой вещи не существует.

С другой стороны, атомарное сохранение работает путем создания нового временного файла, записи в него, а затем rename-записи временного файла поверх исходного файла. Этот rename должен обновить каталог, заменив запись, указывающую на старый файл, записью, указывающей на новый файл. (Конечно, он также отправляет уведомление DELETE для самого файла, потому что файл теряет ссылку. И вы также обычно отправляете ATTRIB, потому что большинство приложений хотят, чтобы новый файл имел такие же расширенные атрибуты, дополнительные разветвления и т. д. .)


* Здесь есть очевидное состояние гонки: если файл перемещается или удаляется между readdir и добавлением его в kqueue, вы получите ошибку. Вы можете захотеть обработать эту ошибку, сгенерировав немедленное уведомление, или, может быть, просто проигнорировать ее — в конце концов, с точки зрения пользователя, это не сильно отличается от случая, когда кто-то удаляет файл между запуском вашей программы и время, когда вы делаете readdir.

person abarnert    schedule 03.05.2015
comment
Я думаю, что утверждение, что kqueue предназначено для переключения десятков тысяч файловых дескрипторов, является преувеличением. Существует ограничение на количество файловых дескрипторов, которые может открыть процесс, и оно, вероятно, не составляет несколько десятков тысяч. В OS X это 10240. Кроме того, если вы просматриваете целую иерархию файлов, вам нужно запускать и останавливать просмотр, когда файлы добавляются и удаляются из иерархии, что нетривиально для эффективного выполнения. - person Ken Thomases; 04.05.2015
comment
@KenThomases Хорошо, вы правы, он был рассчитан на 10000, а не на десятки тысяч; отредактировано. В любом случае, я держу пари, что он заинтересован в просмотре каталогов, содержащих около 100 файлов, и он может просто выдать предупреждение, если каталог больше, чем ожидалось, как я уже сказал. - person abarnert; 04.05.2015
comment
@KenThomases В остальном: я не думаю, что он хочет смотреть всю иерархию, только плоский каталог. И это, во-первых, нетривиально, но очевидный способ сделать это достаточно эффективен. В любом случае, какой другой вариант у него есть, если он хочет писать код, работающий на FreeBSD и OS X? Либо требуется обертка или сервер в стиле gamin, либо сам ставите в очередь весь каталог. - person abarnert; 04.05.2015
comment
Спасибо за такой классный ответ!! Да, в каталоге обычно меньше 100 файлов, но у других пользователей API может быть больше, так что большое спасибо за это. Я думаю, что просто скажу пользователям API, если они хотят наблюдать за перезаписью файла, они должны специально добавить для него наблюдение. - person Noitidart; 04.05.2015
comment
@Noitidart: Если вы создаете что-то для общего использования, вы можете взглянуть на одну из существующих оболочек, которая дает вам FSEvents-подобный интерфейс поверх того, что предоставляет ОС, на всем, от FreeBSD до pre- inotify linux в Windows наиболее эффективным способом (который включает в себя такие вещи, как использование процесса-демона и подпроцессов для управления всеми kqueue fd вместо того, чтобы заставлять это делать каждого клиента). Я не знаю современного состояния таких оберток, но стоит изучить их, а не изобретать велосипед. - person abarnert; 04.05.2015
comment
Спасибо @abarnert! Я проверю это :) - person Noitidart; 04.05.2015