LINUX.ORG.RU

Есть два способа: использовать демон FAM или мониторить вручную (описываю именно этот метод, т.к. считаю fam ненадёжным).

Код ниже мониторит /mydir/mydir2/testix на предмет изменений существующих файлов.

#define SIGNAL (SIGRTMAX-6) /* от SIGRTMIN до SIGRTMAX */
#define DIR "/mydir1/mydir2/testix"
void sigh( int sig )
{
  if(sig == SIGNAL)
  {
     fprintf(stderr, "Файл в " DIR " изменился!");
  }
}

int main( void )
{
  int fd = open(DIR, O_RDONLY | O_DIRECTORY);
  signal(SIGNAL, sigh);
  fcntl(fd, F_SETSIG, SIGNAL); 
  fcntl(fd, F_NOTIFY, DN_MODIFY | DN_DELETE | DN_RENAME | DN_MULTISHOT);

  for(;;)
  {
  }
}


Комментарии к программе: её посылается сигнал SIGNAL, как только в
папке произойдёт одно из событий DN_MODIFY, DN_DELETE или DN_RENAME.
Их значение такое:
 
DN_ACCESS Доступ к файлу (read, pread, readv)
DN_MODIFY Изменение файла (write, pwrite, writev, truncate, ftruncate)
DN_CREATE Создание файла (open, creat, mknod, mkdir, link, symlink, rename)
DN_DELETE Снятие ссылки с файла (unlink, rename to another directory, rmdir)
DN_RENAME Переименование файла внутри каталога (rename)
DN_ATTRIB Изменение атрибутов файла (chown, chmod, utime[s])

Вот. Обычно мониторинг прекращается после первого сигнала, если
не указан флаг DN_MULTISHORT.
Таким образом можно мониторить до 32-х директорий (не анализируя 
данные, переданные сигналу через sigqueue) - с сигналами от SIGRTMIN
до SIGRTMAX.

Чтобы ловить несколько сообщений на один сигнал, можно брать так:
  
 sigaction(SIGNAL, &act, NULL);

а act описать так:

  sigset_t mask;
  sigemptyset(&mask);

  struct sigaction act = {
    .sa_sigaction = sigh;
    .sa_mask = mask;
    .sa_flags = SA_SIGINFO;
  }

При этом sigh примет вид такой:
  void sigh(int sig, siginfo_t *info, void *unused);
  {
    if(sig == SIGNAL)
    {
      int fd_signalled = info->si_fd;
      fprintf(stderr, "Файл в %s изменился!",
          getdirbyfd(fd_signalled));
    }
  }

А getdirbyfd стоит написать самому для поиска в таблице
"имя каталога"-"дескриптор", которая бы заполнялась при старте.


Ещё чуточку напишу - и твоя прога будет готова. :) нет уж, пиши сам. 

 

 

Spectrum
()
Ответ на: комментарий от the_coder

Второй вариант (с sigaction вместо signal) - на Linux, FreeBSD/OpenBSD/NetBSD и, скорее всего, на MacOS X (в последнем неуверен). Возможен вариант с AIX. Вообще, это единственный POSIX-вариант мониторинга, кроме, разумеется, постоянного опроса (который бы жутко тормозил на больших директориях).

Spectrum
()
Ответ на: комментарий от the_coder

Думаю, в WinAPI есть специальные конструкции для таких задач.
К счастью, я о них ничего не знаю (упаси Торвальдс узнать) :-)

Почитай руководство по WinAPI.

Spectrum
()

Загвоздка в том, что если надо мониторить МНОГО директорий, надо держать открытыми МНОГО дескрипторов.

the_coder ★★
() автор топика
Ответ на: комментарий от Spectrum

---cut---
Второй вариант (с sigaction вместо signal) - на Linux, FreeBSD/OpenBSD/NetBSD и, скорее всего, на MacOS X (в последнем неуверен). Возможен вариант с AIX. Вообще, это единственный POSIX-вариант мониторинга, кроме, разумеется, постоянного опроса (который бы жутко тормозил на больших директориях).
---cut---

что-то вы фигню какую то говорите по поводу переносимости..

1. это - не POSIX, и близко не лежало.
http://www.opengroup.org/onlinepubs/009695399/toc.htm

2. как следствие, на BSD это работать не будет.
http://netbsd.gw.com/cgi-bin/man-cgi?fcntl++NetBSD-current
http://www.freebsd.org/cgi/man.cgi?query=fcntl&apropos=0&sektion=0&am...
http://www.openbsd.org/cgi-bin/man.cgi?query=fcntl&apropos=0&sektion=...

3. скорее всего, аналогичное утверждение относится и к прочим, отличным от Linux, системам.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от the_coder

Эти открытые дескрипторы не мешают. Если лимит превышен, то можно его увеличить:
  const struct rlimit fdlim = {
    .rlim_cur = 4096,
    .rlim_max = 4096
  }
  setrlimit(RLIMIT_NOFILE, &fdlim);

(в примере до 4095 дескрипторов) 

Spectrum
()
Ответ на: комментарий от klalafuda

А вам, товарищ klalafuda, я бы больше порекомендовал придумывать реально действующие методы, нежели постить повсюду линк на организацию стандартизации, которая отнюдь не всё правильно делает. Цитирую далее Торвальдса насчёт мыслей этой организации:

"Меня всего беспокоило кое-что относительно O_DIRECT - то, что вообще в целом этот интерфейс просто идиотичен. Создается впечатление, что он как-бы был создан сумасшедшей обезьяной для работы с высокими материями, управляющими разумом." -- Linus

"В _любой_ разумной библиотеке размеры "socklen_t" и int _должны_ совпадать. Любой другой вариант несовместим с реализацией сокетов BSD. В POSIX сначала использовали size_t, но я (и, к счастью, кто-то еще, хотя и не слишком многие) сильно возмутились по этому поводу. Такая реализация полностью поломана как раз потому, что size_t очень редко имеет тот же размер, что и "int", например, в 64-битных архитектурах. Это необходимо потому, что интерфейс сокетов BSD таков, каков он есть. В любом случае, люди из POSIX наконец поняли и создали "socklen_t". Вообще, с самого начала они просто не должны были ничего трогать, но по какой-то причине они чувствовали, что должны использовать поименованный тип (вероятно, они не хотели ударить в грязь лицом, сделав глупость, поэтому они тихо переименовали свою грубую ошибку)." -- Linus

Spectrum
()
Ответ на: комментарий от Spectrum

---cut---
А вам, товарищ klalafuda, я бы больше порекомендовал придумывать реально действующие методы, нежели постить повсюду линк на организацию стандартизации, которая отнюдь не всё правильно делает.
---cut---

а работать за вас Линус будет? в том числе разгребать завалы доморощенных API? или вы ограничены лишь его творением? мои соболезнования :-/

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от Spectrum

---cut---
"Меня всего беспокоило кое-что относительно O_DIRECT - то, что вообще в целом этот интерфейс просто идиотичен. Создается впечатление, что он как-бы был создан сумасшедшей обезьяной для работы с высокими материями, управляющими разумом." -- Linus
---cut---

я так понимаю, это флаг для open(2)? что-то я не вижу его ни в POSIX, ни в NetBSD, ни в Solaris и иже с ними. откуда это?

http://www.opengroup.org/onlinepubs/009695399/functions/open.html
http://netbsd.gw.com/cgi-bin/man-cgi?open++NetBSD-current
http://docs.sun.com/app/docs/doc/802-5747-02/6i9g1b6a9?q=open%282%29&a=view

зато google на O_DIRECT пестрит ссылками именно на Linux.
это чаем не его нововведение и каким боком это к POSIX?

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

Ни в коем случае. Вообще, не мешай людям работать. Человек попросил метод мониторинга - я его описал. Ты что, все файлы будешь рекурсивно опрашивать? Похоже, батенька, вы виндузник.

Spectrum
()
Ответ на: комментарий от Spectrum

> Ни в коем случае. Вообще, не мешай людям работать. Человек попросил метод мониторинга - я его описал.

про FAM вам уже сказали. про непереносимость предложенного метода - то-же. я могу, например, предложить kqueue и он, вполне возможно, будет шустрее и надежнее FAM. но в 95% случаев это отнюдь не выход.

> Ты что, все файлы будешь рекурсивно опрашивать? Похоже, батенька, вы виндузник.

как *я* это буду делать - не вам судить.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

Пока что дельных предложений от тебя не услышано, так что предлагаю прекратить этот бессмысленный (!) спор до появления оных.

Spectrum
()
Ответ на: комментарий от the_coder

Кстати, про переносимость. Я и правда нашёл одну часть кода, работающую только на Линукс:
  fcntl(fd, F_SETSIG, SIGNAL); 
На всех юниксах вместо SIGNAL будет возвращаться SIGIO, что не совсем
хорошо (при большой активности оповещений некоторые сигналы могут
теряться), но в целом допустимо.



Spectrum
()
Ответ на: комментарий от Spectrum

---cut---
Кстати, про переносимость. Я и правда нашёл одну часть кода, работающую только на Линукс:
fcntl(fd, F_SETSIG, SIGNAL);
---cut---

не прошло и полугода...
это - единственная и принципиальная вещь, вызывающая у меня нарекание в приведенном коде бо "Linux only".

---cut---
На всех юниксах вместо SIGNAL будет возвращаться SIGIO, что не совсем
хорошо (при большой активности оповещений некоторые сигналы могут
теряться), но в целом допустимо.
---cut---

вот мы и подошли к теме. можно ссылку на man в студию с указанием "*NIX XYZ поддерживает F_SETSIG для fcntl(2)"? а заодно не забудте просветить на тему про SIGIO (это что-то новое).

для разнообразия начните с *BSD, SUN, IBM и HP. ну а если вы найдете это в POSIX или SUSV3 я вам поставлю памятник. рукотворный.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от Spectrum

> Юниксы не поддерживают F_SETSIG,

разумно

> а всегда используют SIGIO для fcntl-оповещений.

..но весьма неполно. можно услышать продолжение мысли?
а лучше всего маленький пример кода, который бы работал на *NIX отличном от Linux (лучше aka проще BSD), и который бы отслеживал изменение состояния директории через пару fcntl()/SIGIO или, бог с ним, любую другую. желательно, чтобы это было достаточно надежно и переносимо.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от jackLucas

По просьбе jackLucas описываю интерфейс inotify
(сразу предупреждаю: это linux only, причём >= 2.6.13)

 int ihandle = inotify_init();
 inotify_add_watch(ihandle, "/mydir1/mydir2/testix", events);

включает оповещения об events на указанной директории.
К одному ihandle можно подключать много директорий.

Параметр events будет суммой одного из следующих флагов:
 IN_CREATE
 IN_DELETE
 IN_ACCESS
 IN_ATTRIB
 IN_MODIFY
 IN_MOVED_FROM
 IN_MOVED_TO
 IN_DELETE_SELF
(перечислены лишь те, которые имеют смысл при мониторинге директорий)
Флаг IN_ISDIR следит, например, за удалением подкаталогов.

Как получать сообщения Linux inotify? Скажем так, в системе есть
специальное устройство inotify, название которого мне точно неизвестно
(скажем, /dev/inotify). Так вот, из этого устройства можно читать вот
такие структуры:
 
  struct inotify_event {
      __s32 ihandle; /* наш ihandle */
      __u32 events; /* что, собственно, случилось */
      __u32 cookie; /* какая-то лабуда */

      __u32 len; /* длина поля charname */
      charname[0]; /* поле длиной len, пока не используется */
  };

Итак, 
    struct inotify_event ev;
    int fd = open("/dev/inotify", F_RDONLY);
    while(read(fd, &ev, 12) == 16)
    { /* считали первые четыре поля */
        seek(fd, ev.len, SEEK_CUR); /* пропускаем */

        fprintf(stdout, "Событие обнаружено!\n");
        /* тут что-то делается */
    }

Вот. Ещё раз повторяю: inotify - только для Linux >= 2.6.13.


Spectrum
()
Ответ на: комментарий от Spectrum

вот это уже значительно лучше: чесно указаны вполне реалистичные границы применимости решения -> дальше это уже проблема конкретного применителя :)

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от Spectrum

#include чего надо сделать, чтобы получить inotify_init(); ?

и чего подключить -l... gcc для сборки?

про то, что это под линукс 2.6.13 и выше я в курсе :)

там (в inotify) вроде API ломали? то ioctl, то inotify_init? или я что-то путаю?

P.S. в ядре inotify включено. :)

jackLucas
()
Ответ на: комментарий от jackLucas

1. #include <inotify.h> 2. Не надо подключать никаких библиотек. 3. Я немножко ошибся, файла по типу /dev/inotify нет, а читать надо из дескриптора, возвращённого inotify_init. 4. Выше описывается последний стандарт API, из заголовков и манов последней версии.

Spectrum
()
Ответ на: комментарий от Spectrum

я извиняюсь, но где взять inotify.h? в тех inotify.h, которые в исходниках ядра, нет inotify_init :(

извиняюсь, если надоел своими вопросами :(, но хочется разобраться

jackLucas
()
Ответ на: комментарий от jackLucas

Да, там и правда нету прототипа. Ну его, напиши сам в начале программы

  int inotify_init( void );
  int inotify_add_watch(int ihandle, const char *path, __u32 events);
  int inotify_rm_watch(int ihandle, const char *path, __u32 events);

Spectrum
()
Ответ на: комментарий от Spectrum

ну и что, написал я так.

gcc выдал:

/tmp/ccvBeovx.o(.text+0x1d): In function `main':

: undefined reference to `inotify_init'

collect2: ld returned 1 exit status

и он абсолютно прав, где ему взять тела функций inotify_* ?

я извиняюсь за дотошность, но раз уж начал объяснять, доведи дело до конца :)

чуть не забыл: волшебное слово - пожалуйста :)

jackLucas
()
Ответ на: комментарий от jackLucas

Ну да, это же вызовы ядра. В начале программы:

#include <sys/syscall.h>

syscall0(int, inotify_init);
syscall3(int, inotify_add_queue, int, ifd, const char *, dir, __u32, mask);

Spectrum
()
Ответ на: комментарий от Spectrum

программа:

#include <sys/syscall.h>

syscall0(int, inotify_init);

syscall3(int, inotify_add_queue, int, ifd, const char *, dir, __u32, mask);

int inotify_init( void );

int

main (int argc, char **argv)

{

int ihandle = inotify_init();

return (0);

}

gcc -o exe 1.c :

1.c:20: error: parse error before "inotify_init"

1.c:20: warning: data definition has no type or storage class

1.c:21: error: parse error before "inotify_add_queue"

1.c:21: warning: data definition has no type or storage class

20 и 21 - строки с syscall'ами. я раньше syscall никогда не пользовался, в чём проблема?

jackLucas
()
Ответ на: комментарий от jackLucas

Дико извиняюсь, я допустил ошибку.
Не syscall0/syscall3, а _syscall0/_syscall3.
Это макроопределения, они находятся в <linux/unistd.h>

На случай, если их не найдутся:

#define _syscall0(type,name) \
type name(void) \
{\
	return syscall(__NR_##name);\
}

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{\
	return syscall(__NR_##name, arg1, arg2, arg3);\
}

Spectrum
()
Ответ на: комментарий от Spectrum

пасибо. я уже успел google'м найти inotify.h (с описанием типов данных) и inotify-syscalls.h где прописаны эти самые syscall'ы :)

там это описано так:

static inline int inotify_init (void)

{

return syscall (__NR_inotify_init);

}

и т.д.

так что ими и пользуюсь :)

P.S. спасибо за помощь :)

jackLucas
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.