Решено: Inotify (Ожидание поступления событий в струтуру inotify)

Модератор: Модераторы разделов

chegivara
Сообщения: 3

Решено: Inotify

Сообщение chegivara »

Здравствуйте.
Пишу программу с использованием системного вызова inotify( мониторит указанные файлы на события ). Вызов пишет результат в структуру inotify_event.
Необходимо, чтобы программа постоянно ожидала события, т е ждёт когда появятся новые события в структуре. Единственным способом ( как я понимаю ) в ожидании событий, это проверка размера стуктуры.
Получился такой цикл:

Код: Выделить всё

                do
                {
                rc = ioctl(fd, FIONREAD, &queue_len);
                }
                while( !rc && queue_len < sizeof(struct inotify_event));


fd - Дескриптор вызова inotify ( fd = inotify_init(); )
C помощью функции ioctl, получаем queue_len - размер очереди в байтах, и на основе размера стуктуры, делаем вывод о количестве ожидаемых событий.
Всё хорошо, но такой цикл отжирает 100% проца. Можно добавить sleep() и проблема решена.
Я новичок в программировании, и прошу совета правильно ли будет решение с использованием функции sleep?
Существует пакет inotify_tools http://inotify-tools.sourceforge.net/ , в состав которого входит программа inotifywait, она мониторит файлы на события, с последующим выводом на STDOUT результата. В коде этой программы есть похожий цикл, но каким образом устроено ожидание событий не могу разобраться.

Для понятности вот полный код моей программы:

Код: Выделить всё

#include <stdio.h>
#include <stdlib.h>
#include <linux/inotify.h>
#include <sys/ioctl.h>

int
main(int argc, char *argv[])
{
        /*Инициализация inotify*/
        int fd;
        fd = inotify_init();
        if(fd < 0)
        {
                perror("inotify_init");
                return 1;
        }
         /*Вызов стража*/
        int wd;
        wd = inotify_add_watch(fd, argv[1], IN_MODIFY | IN_MOVE_SELF);
        if(wd < 0)
        {
                perror("inotify_add_watch");
                return 1;
        }

        while(1)
        {
                char *buf;
                struct inotify_event *event;
                unsigned int queue_len;
                int rc;

                do
                {
                rc = ioctl(fd, FIONREAD, &queue_len);
                sleep(1);
                printf (" Ожидает в очереди %u байтов\n", queue_len);
                }

                while( !rc && queue_len < sizeof(struct inotify_event));
                buf = malloc(queue_len); /* buf ссылается на первый байт выделенного участка памяти */
                if(read(fd, buf, queue_len) == -1)
                {
                        perror("read");
                        return 1;
                }
                if ( event->mask & IN_MODIFY | IN_MOVE_SELF )
                       printf ( " Файл был изменён\n " );
                int i;
                for(i = 0; i < queue_len; i += event->len + sizeof(struct inotify_event))
                {
                        event = (struct inotify_event *) (buf + i); /* event ссылается на (buf + i)  */
                           printf("wd = %d, mask = %08X, cookie = %d, len = %d, name = %s\n",
                                      event->wd, event->mask, event->cookie, event->len, &event->name);
                           printf (" Размер стуктуры inotify:%d\n", sizeof(struct inotify_event));

                }

                free(buf);
        }

         return 0;
}


Результат:
Ожидает в очереди 16 байтов
Файл был изменён
wd = 1, mask = 00000800, cookie = 0, len = 0, name =
Размер стуктуры inotify:16


Вообщем прошу совета, каким образом правильно реализовать ожидание событий.
P.s
Если, что то не понятно обьяснил, говорите. Я новичок в кодинге.
Спасибо сказали:
Аватара пользователя
Rootlexx
Бывший модератор
Сообщения: 4471
Статус: GNU generation
ОС: Debian GNU/Linux

Re: Решено: Inotify

Сообщение Rootlexx »

chegivara
Используйте poll или select (худший из вариантов в плане быстродействия, но хорошо портируемый) или epoll (только Linux; лучший по быстродействию, отлично масштабируется в отличие от первых двух) для мониторинга дескриптора fd. man 2 select, man 2 poll, man 7 epoll.

Добавлено: посмотрите ещё man 2 fcntl, команда F_NOTIFY.
Спасибо сказали:
Galaxy Master
Сообщения: 142
ОС: Debian GNU/Linux

Re: Решено: Inotify

Сообщение Galaxy Master »

прикольно, читать, как "новички" в ioctl сразу лезут...

почему бы вместо "Hello world" не начать с правки ядра?
...а как сломается.. - бегом на форум спросить, "что я делаю не так"...

это конечно оффтоп.... но уж улыбнуло (без обид)


PS. а по делу... читайте пост выше. и мониторьте без гемора
Спасибо сказали:
chegivara
Сообщения: 3

Re: Решено: Inotify

Сообщение chegivara »

Спасибо за ответы.
Следуя вашим советом попробовал использовать вызов select:

Код: Выделить всё

        while( 1 )
        {
                char *buf;
                struct inotify_event *event;
                unsigned int queue_len;
                int rc;

                FD_ZERO(&readfds);
                FD_SET( fd, &readfds );
                ret = select ( fd + 1, &readfds, NULL, NULL, &tv );
                if ( ret < 0 )
                {
                        perror ( "select" );
                }
                else if ( !ret )
                {
                        printf ( "%d secinds elapsed.\n", TIMEOUT );
                        return 0;
                }
                else if ( FD_ISSET (fd, &readfds))
                buf = malloc( queue_len );
                if( read( fd, buf, queue_len ) == -1 )
                {
                        perror( "read" );
                        return 1;
                }
                if ( event->mask & IN_MODIFY | IN_MOVE_SELF )
                       printf ( " Файл был изменён\n " );
                int i;
                for( i = 0; i < queue_len; i += event->len + sizeof( struct inotify_event ) )
                {
                        event = ( struct inotify_event * ) ( buf + i );
                           printf( "wd = %d, mask = %08X, cookie = %d, len = %d, name = %s\n",
                                      event->wd, event->mask, event->cookie, event->len, &event->name );
                           printf ("Размер стуктуры inotify:%d\n", sizeof( struct inotify_event ) );
                        if ( event->len )
                        {
                        printf ("name=%s\n", event->name);
                        }
                }
                free( buf );
        }

         return 0;
}

Я выставляю select на дескриптор inotify - fd.
Теперь мой процесс замораживается до тех пор, пока не появятся данные для считывания. После появления данных, мой цикл двигается дальше, это хорошо, но при новой отработке цикла, select естественно пропускает меня дальше и у меня получается бесконечный цикл, Посоветуйте как здесь быть? Наверное необходимо каким то образом очищать данные на fd.
Спасибо сказали:
Аватара пользователя
Rootlexx
Бывший модератор
Сообщения: 4471
Статус: GNU generation
ОС: Debian GNU/Linux

Re: Решено: Inotify

Сообщение Rootlexx »

chegivara
Такой вопрос: раньше у вас значение переменной queue_len устанавливалось вызовом ioctl(). В новом варианте цикла переменная не инициализируется каким-либо значением. Поэтому возможно, что read() считывает недостаточное (0?) количество байт, чтобы запись о событии исчезла с дескриптора fd.
В качестве примера использования inotify можете посмотреть это.
Спасибо сказали:
chegivara
Сообщения: 3

Re: Решено: Inotify

Сообщение chegivara »

Всем спасибо за помощь. Проблема решена, в принципе первый же ответ в этом топике и есть решение, остальное время я тормозил, полное отсутствие опыта.
Спасибо сказали: