На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила трёх "С"
Пожалуйста,
1. Соблюдайте правила Форума.
2. Слушайте советы Модераторов.
(например, http://forum.sources.ru/index.php?act=ST&f=7&t=80382 )
3. Сверяйтесь с учебником по Великому и Могучему
  
> Определение наличия данных в stdin
    Есть такая проблемка: пишется прога для FreeBSD на сюхе, которая забирает данные по конвееру из сислога. Для этого надо определять есть ли данные в stdin. Если просто опрашивать stdin, то прога сильно грузит процессор. Нужен совет как сделать так, чтобы прога находилась в спящем режиме, а при поступлении данных в stdin - просыпалась?

    Попоробывал вот такое решение проблемы:
     int iRes_stdin, iStdin;
     struct timeval timeout;
     fd_set readfd;

     //definition of presence of the data in stdin
     iStdin=fileno(stdin);  
     FD_ZERO(&readfd);
     FD_SET(iStdin, &readfd);
     timeout.tv_sec = 60;
     timeout.tv_usec = 60000;

     iRes_stdin = select(iStdin + 1, &readfd, NULL, NULL, &timeout);
     if (iRes_stdin > 0 )
     {
         //забираю данные
     }


    По идее функция select() определяет наличие готовых к чтению дескрипоторов и тогда возвращает положительное значение. Если таймаут истек то возвращается ноль. Вроде все подходит, но почему-то select()всегда возвращает, что дескриптор stdin готов для чтения - даже когда в потоке нет данных. В чем ошибка?
      Я бы, честно говоря, опрашивал бы порты, причем на asm'е, если связано с клавой. А с файлами в Unix-овых платформах я никогда не работал :-/
      Сообщение отредактировано: Alch -
        Да я получаю данные по конвееру из сислога, так что порты не помогут
          Код правильный, хотя в конце лучше бы написать так:

           iRes_stdin = select(iStdin + 1, &readfd, NULL, NULL, &timeout);
           if (iRes_stdin > 0 )
           {
              if( FD_ISSET(iStdin, &readfd) )
              {
                 //забираю данные
              }
           }

          Хотя по идее, если ты проверяешь только один дескриптор, то проверки (iRes_stdin > 0 ) должно быть достаточно, ведь количество готовых дескрипторов или 0 или 1...

          Если данную проблему тебе победить не удастся, то могу порекомендовать вместо select() использовать poll() или kqueue() - причем последний является прерогативой только BSD, и соответственно непортабелен. Зато не надо будет fdset-ы заново создавать перед каждым ожиданием данных.

          PS: timeout.tv_usec можно нулю приравнять, если хочешь опрашивать дескриптор не дольше 60 секунд.
            Если данную проблему тебе победить не удастся, то могу порекомендовать вместо select() использовать poll() или kqueue() - причем последний является прерогативой только BSD, и соответственно непортабелен. Зато не надо будет fdset-ы заново создавать перед каждым ожиданием данных.

              Я попробовал использовать poll(), получается такая же ситуация, при пустом потоке функция возвращает что дескриптор потока готов для чтения.
            Код вот такой:
             char cmdsql[254]
             struct pollfd fds;
             fds.fd=iStdin;
             fds.events=POLLRDNORM;
             for(;;)
             {
              iRes_stdin = poll(&fds,1,60000);
              if (iRes_stdin > 0 )
               {
                 p=fgets(cmdsql,254,stdin);
                 if(p!=NULL)
                   printf("\%s",cmdsql);
               }
             }

            Может че неправильно?
              Более корректно после вызова poll() делать такую проверку -
                if (iRes_stdin > 0)
                {
                     if( fds.revents & POLLRDNORM)
                     {
                        // обрабатываемся...
                     }
                     else
                     {
                        // мистика какая-то....
                     }
                }

              Еще попробуй использовать константу POLLNORM вместо POLLRDNORM в обоих случаях. Может поможет. Какая у тебя версия FreeBSD?

              Слушай, а может у тебя тайм-аут истекает 60-ти секундный?

              PS: Кстати по поводу твоего последнего примера - там по-прежнему iStdin=fileno(stdin)?
              PPS: В моем man-е написано, что если дескриптор потока - нормальный файл, то POLLNORM всегда будет рапортовать о готовности к неблокирующему чтению. Может дело в этом? У тебя конвейер как построен - "|" в шелле или что-то другое?
              PPPS: Есть напоследок еще один способ - если poll() все-таки не поможет - расскажу.
                >>Еще попробуй использовать константу POLLNORM вместо POLLRDNORM в обоих >>случаях. Может поможет. Какая у тебя версия FreeBSD?
                  FreeBSD 4.6

                >>Слушай, а может у тебя тайм-аут истекает 60-ти секундный?
                  так таймаут же по нулевому значению отлавливается (по истечению таймаута poll возвращает ноль)

                >>PS: Кстати по поводу твоего последнего примера - там по-прежнему iStdin=fileno(stdin)?
                     да там именно так, но можно и iStdin=0;

                >>PPS: В моем man-е написано, что если дескриптор потока - нормальный файл, то >>POLLNORM всегда будет рапортовать о готовности к неблокирующему чтению. Может >>дело в этом?
                  Да наверное дело в этом, а как с этим бороться?

                >>У тебя конвейер как построен - "|" в шелле или что-то другое?
                  Конвеер построен так: демон через сислог выдает сообщения, а сислог посылает их моей проге (*.info | program)

                  Насчет таймаута ты прав - я ошибся.

                  >> PPS: В моем man-е написано, что если дескриптор потока - нормальный файл,
                  >> то POLLNORM всегда будет рапортовать о готовности к неблокирующему
                  >> чтению. Может дело в этом?  
                  > Да наверное дело в этом, а как с этим бороться?
                  В твоем случае имеет место чтение из сокета, а не из регулярного файла, то есть poll() должон работать. На днях проапгрейдюсь до 4.6 и попробую.

                  Последний способ, который можно попробовать - установить на stdin аттрибут неблокируемости через fcntl(...O_NONBLOCK... ) - при попытки чтения (read() на самом низком уровне) процесс не будет блокироваться, а системный вызов read() будет возвращать -1 и выставлять errno в EAGAIN. В этом случае ты можешь сделать usleep() секунд на 60 и повторить снова...

                  Это самый тупой и некрасивый способ, но если и он не сработает - то значит имеет место мистика. Сушим весла.

                    >> PPS: В моем man-е написано, что если дескриптор потока - нормальный файл,
                    >> то POLLNORM всегда будет рапортовать о готовности к неблокирующему
                    >> чтению. Может дело в этом?  
                    > Да наверное дело в этом, а как с этим бороться?
                    >>В твоем случае имеет место чтение из сокета, а не из регулярного файла, то есть poll?>>() должон работать. На днях проапгрейдюсь до 4.6 и попробую.

                    А почему именно из сокета? Может система считает его нормальным файлом? А для файла как можно это побороть?
                      Привет, kong.

                      Если для ожидания чтения из файла (точнее дескриптора) не сработает дубовый способ, описанный в моем предыдущем постинге, то есть еще один способ -

                      Перед потенциально мо'гущим зависнуть чтением поставь себе аларм на 1-2 секунды:
                        #include <unistd.h>
                        unsigned int alarm(unsigned int sec);
                      По прошествии этого времени, твой процесс получит сигнал SIGALRM, который (если не игнорируется) прервет висящий read() - тот вернет -1 и выставит errno в EINTR.

                      PS: Повесить обработчик на сигнал можно разными способами, например
                       #include <signal.h>
                       void (*signal(int sig, void (*func)(int)))(int); или
                       void (*sigset(int sig, void (*disp)(int)))(int);
                      в твоем случае обработчик может быть максимально простым - делать только return, ведь у него единственная задача - прервать системный вызов read().

                      PPS: Заговорил о сигналах и вспомнил, что еще есть способ назначить файловому дескриптору сигнал SIGIO (или SIGUSR1/SIGUSR2), который будет посылаться твоему процессу на каждую пришедшую в дескриптор порцию данных, которые можно считать. Это зовется асинхронный I/O и я затрудняюсь вспомнить, какие функции надо вызывать чтобы активировать такую фичу - делал я это еще в универе, лет 5 назад под линуксом.
                      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                      0 пользователей:


                      Рейтинг@Mail.ru
                      [ Script execution time: 0,0422 ]   [ 15 queries used ]   [ Generated: 27.04.24, 17:00 GMT ]