Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.145.58.169] |
|
Сообщ.
#1
,
|
|
|
Есть такая проблемка: пишется прога для 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 готов для чтения - даже когда в потоке нет данных. В чем ошибка? |
Сообщ.
#2
,
|
|
|
Я бы, честно говоря, опрашивал бы порты, причем на asm'е, если связано с клавой. А с файлами в Unix-овых платформах я никогда не работал :-/
|
Сообщ.
#3
,
|
|
|
Да я получаю данные по конвееру из сислога, так что порты не помогут
|
Сообщ.
#4
,
|
|
|
Код правильный, хотя в конце лучше бы написать так:
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 секунд. |
Сообщ.
#5
,
|
|
|
Если данную проблему тебе победить не удастся, то могу порекомендовать вместо 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); } } Может че неправильно? |
Сообщ.
#6
,
|
|
|
Более корректно после вызова poll() делать такую проверку -
if (iRes_stdin > 0) { if( fds.revents & POLLRDNORM) { // обрабатываемся... } else { // мистика какая-то.... } } Еще попробуй использовать константу POLLNORM вместо POLLRDNORM в обоих случаях. Может поможет. Какая у тебя версия FreeBSD? Слушай, а может у тебя тайм-аут истекает 60-ти секундный? PS: Кстати по поводу твоего последнего примера - там по-прежнему iStdin=fileno(stdin)? PPS: В моем man-е написано, что если дескриптор потока - нормальный файл, то POLLNORM всегда будет рапортовать о готовности к неблокирующему чтению. Может дело в этом? У тебя конвейер как построен - "|" в шелле или что-то другое? PPPS: Есть напоследок еще один способ - если poll() все-таки не поможет - расскажу. |
Сообщ.
#7
,
|
|
|
>>Еще попробуй использовать константу POLLNORM вместо POLLRDNORM в обоих >>случаях. Может поможет. Какая у тебя версия FreeBSD?
FreeBSD 4.6 >>Слушай, а может у тебя тайм-аут истекает 60-ти секундный? так таймаут же по нулевому значению отлавливается (по истечению таймаута poll возвращает ноль) >>PS: Кстати по поводу твоего последнего примера - там по-прежнему iStdin=fileno(stdin)? да там именно так, но можно и iStdin=0; >>PPS: В моем man-е написано, что если дескриптор потока - нормальный файл, то >>POLLNORM всегда будет рапортовать о готовности к неблокирующему чтению. Может >>дело в этом? Да наверное дело в этом, а как с этим бороться? >>У тебя конвейер как построен - "|" в шелле или что-то другое? Конвеер построен так: демон через сислог выдает сообщения, а сислог посылает их моей проге (*.info | program) |
Сообщ.
#8
,
|
|
|
Насчет таймаута ты прав - я ошибся.
>> PPS: В моем man-е написано, что если дескриптор потока - нормальный файл, >> то POLLNORM всегда будет рапортовать о готовности к неблокирующему >> чтению. Может дело в этом? > Да наверное дело в этом, а как с этим бороться? В твоем случае имеет место чтение из сокета, а не из регулярного файла, то есть poll() должон работать. На днях проапгрейдюсь до 4.6 и попробую. Последний способ, который можно попробовать - установить на stdin аттрибут неблокируемости через fcntl(...O_NONBLOCK... ) - при попытки чтения (read() на самом низком уровне) процесс не будет блокироваться, а системный вызов read() будет возвращать -1 и выставлять errno в EAGAIN. В этом случае ты можешь сделать usleep() секунд на 60 и повторить снова... Это самый тупой и некрасивый способ, но если и он не сработает - то значит имеет место мистика. Сушим весла. |
Сообщ.
#9
,
|
|
|
>> PPS: В моем man-е написано, что если дескриптор потока - нормальный файл, >> то POLLNORM всегда будет рапортовать о готовности к неблокирующему >> чтению. Может дело в этом? > Да наверное дело в этом, а как с этим бороться? >>В твоем случае имеет место чтение из сокета, а не из регулярного файла, то есть poll?>>() должон работать. На днях проапгрейдюсь до 4.6 и попробую. А почему именно из сокета? Может система считает его нормальным файлом? А для файла как можно это побороть? |
Сообщ.
#10
,
|
|
|
Привет, 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 назад под линуксом. |