Моя программа (на C ++) использует цикл событий libev. И мне нужно смотреть на определенную папку (скажем, foo) для новых файлов.
Я не могу использовать Inotify :: WaitForEvents () в режиме блокировки, потому что я не хочу блокировать цикл обработки событий libev. Как предложено в документация inotify,Я использую Inotify :: SetNonBlock (true), чтобы сделать его неблокированным. Дескриптор файла inotify затем передается в libev EV_STAT для просмотра (как предложено в либеральная документация).
Обратный вызов libev для EV_STAT действительно вызывается, когда в папке foo появляются новые файлы. Однако, когда я использую Inotify :: WaitForEvents (), за которым следует Inotify :: GetEventCount (), я получаю нулевое событие.
Я подозреваю, что libev уже использовал событие и преобразовал его в событие EV_STAT. Если это так, как я могу получить имена этих новых файлов?
Я знал, что в параметрах обратного вызова EV_STAT есть номер инода, но получить имя файла по номеру инода нетривиально. Так что лучше, если я получу имя файла.
Какие-либо предложения?
Я написал небольшую программу для воспроизведения этой проблемы. Кажется, события не потеряны. Вместо этого события inotify еще не наступают, когда вызывается callback libev. Событие может появиться снова при копировании в новый файл.
Программа для воспроизведения вопроса:
#include <ev++.h>
#include "inotify-cxx.h"#include <iostream>
const char * path_to_watch = "/path/to/my/folder";
class ev_inotify_test
{
InotifyWatch m_watch;
Inotify m_notify;
// for watching new files
ev::stat m_folderWatcher;
public:
ev_inotify_test() : m_watch(path_to_watch, IN_MOVED_TO | IN_CLOSE_WRITE),
m_notify()
{
}
void run()
{
try {
start();
// run the loop
ev::get_default_loop().run(0);
}
catch (InotifyException & e) {
std::cout << e.GetMessage() << std::endl;
}
catch (...) {
std::cout << "got an unknown exception." << std::endl;
}
}
private:
void start()
{
m_notify.SetNonBlock(true);
m_notify.Add(m_watch);
m_folderWatcher.set<ev_inotify_test, &ev_inotify_test::cb_stat>(this);
m_folderWatcher.set(path_to_watch);
m_folderWatcher.start();
}void cb_stat(ev::stat &w, int revents)
{
std::cout << "cb_stat called" << std::endl;
try {
m_notify.WaitForEvents();
size_t count = m_notify.GetEventCount();
std::cout << "inotify got " << count << " event(s).\n";
while (count > 0) {
InotifyEvent event;
bool got_event = m_notify.GetEvent(&event);
std::cout << "inotify confirm got event" << std::endl;
if (got_event) {
std::string filename = event.GetName();
std::cout << "test: inotify got file " << filename << std::endl;
}
--count;
}
}
catch (InotifyException &e) {
std::cout << "inotify exception occurred: " << e.GetMessage() << std::endl;
}
catch (...) {
std::cout << "Unknown exception in inotify processing occurred!" << std::endl;
}
}
};int main(int argc, char ** argv)
{
ev_inotify_test().run();
}
Когда я копирую крошечный файл (скажем, 300 байт), файл обнаруживается немедленно. Но если я скопирую файл большего размера (скажем, 500 КБ), события не произойдет, пока я не скопирую другой файл, и тогда я получу два события.
Вывод выглядит так:
cb_stat called # test_file_1 (300 bytes) is copied in
inotify got 1 event(s).
inotify confirm got event
test: inotify got file test_file_1
cb_stat called # test_file_2 (500 KB) is copied in
inotify got 0 event(s). # no inotify event
cb_stat called # test_file_3 (300 bytes) is copied in
inotify got 2 event(s).
inotify confirm got event
test: inotify got file test_file_2
inotify confirm got event
test: inotify got file test_file_3
Я наконец-то понял проблему: мне нужно использовать ev :: io для просмотра файлового дескриптора inotify вместо использования ev :: stat для просмотра папки.
В примере кода определение m_folderWatcher
должно быть:
ev::io m_folderWatcher;
вместо
ev::stat m_folderWatcher;
И это должно быть инициализировано как:
m_folderWatcher.set(m_notify.GetDescriptor(), ev::READ);
вместо
m_folderWatcher.set(path_to_watch);
Других решений пока нет …