Hooter
|
|
« : 10-05-2006 06:45 » |
|
Ситуация следующая. Есть головной процесс, который запускает дочерние процессы и перехватывает потоки вывода (out и err) этих дочерних процессов. Головной процесс с помощью select() ожидает появления каких-либо данных в потоках вывода дочерних процессов, читает эти данные и выводит их в файл. Проблема в том, что данные в потоках вывода не всегда перехватываются головным процессом. Совершенно точно известно, что один из дочерних процессов всегда выводит текст встандартный поток вывода и поток ошибок (это проверяется, если не перехватывать потоки в головном процессе). Но при перехвате данных в потоке головным процессом видно, что иногда данные перехватываются, а иногда - нет. То есть такое впечатление, что иногда данные просто не поступают от дочернего процесса. Я смог локализовать условия, когда возникает такая ситуация: если перед запуском выполнить пересборку дочерней программы - вывод не перехватывается. Если не пересобирать дочернюю программу, а запускать уже существующую - всё нормально. Но мне это ни о чём не говорит Под пересборкой программы я подразумеваю просто пересборку программы без каких-либо ёё изменений. Кто сталкивался с подобным поведением? В чем может быть проблема?
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #1 : 10-05-2006 06:58 » |
|
Hooter, какие ф-ии используются для вывода? Прямой write() или потоковые? Может перед завершением сделать сброс буферов?
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Hooter
|
|
« Ответ #2 : 10-05-2006 07:07 » |
|
В конечном счете вызывается write. Делал вызов flush - то же самое.
|
|
« Последнее редактирование: 10-05-2006 07:10 от Hooter »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #3 : 10-05-2006 07:19 » |
|
Сдается мне, в программе ошибка. Всегда, когда у меня происходило что-то подобное, оказывалось, что где-то маленькая ошибка, типа перепутанных переменных. Попробуй простые тесты написать (только не copy-paste) для клиента и сервера. Тест-клиент должен выполнить один write() и завершиться, а тест-сервер должен запустить и втупую принять из потока.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Hooter
|
|
« Ответ #4 : 10-05-2006 07:20 » |
|
Я прошу прощения, в конечном счете вызывается fwrite, а не write.
|
|
|
Записан
|
|
|
|
Hooter
|
|
« Ответ #5 : 10-05-2006 07:24 » |
|
...Всегда, когда у меня происходило что-то подобное, оказывалось, что где-то маленькая ошибка, типа перепутанных переменных. ...
Не исключено, что ошибка, но как тогда объяснить этот фокус с пересборкой? То есть, если пересобирать дочерний модуль - потоки не перехватываются. Если же запускать после этого программу без пересборки - то все последующие запуски показывают, что все нормально... Может быть это как-то связано с инициализацией?
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #6 : 10-05-2006 07:30 » |
|
Hooter, а главная программа у тебя постоянно загружена? После пересборки ты ее останавливаешь?
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Hooter
|
|
« Ответ #7 : 10-05-2006 08:32 » |
|
Останавливаю с завершением всех дочерних процессов. То есть каждый раз всё запускается по новой.
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #8 : 10-05-2006 09:05 » |
|
Т.е. и нормальные (которые не работают) запуски то же целиком с остановкой?
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Hooter
|
|
« Ответ #9 : 10-05-2006 10:46 » |
|
Да, именно так.
|
|
|
Записан
|
|
|
|
Hooter
|
|
« Ответ #10 : 10-05-2006 13:10 » |
|
Попытался написать небольшой пример на С. Кажется, я что-то делаю неправильно... Вот пример. В примере пытаюсь запустить дочерним процессом "ps" и перехватить его вывод. Пайпы использовал здесь потому, что в реальной задаче нужно перенаправлять и поток вывода и поток ошибок нескольких процессов. В данном примере только один процесс и перенаправление только одного потока вывода. #include <fcntl.h> #include <stdio.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h>
const char * const Child_Str = "ps";
int main (int argc, char **argv) { printf("Test 04-000-0000\n");
pid_t pid; int opipe[2];
if (-1 == pipe(opipe)) { printf("Error: creating pipe"); return -1; }
//----------------------------------------------------------------------- // Create a child process
pid = fork();
switch (pid) { case -1: printf("Error: fork()\n"); return -1;
case 0: close(opipe[1]); break;
default: // redirect the standard output to the pipe close(1); dup2(opipe[1], 1); close(opipe[0]); close(opipe[1]);
// run the child process printf("run child process\n\n"); execlp(Child_Str, Child_Str, 0); printf("Error: run %s\n", Child_Str);
break; }
//----------------------------------------------------------------------- // Catch the output
const time_t t = time(0); fd_set pipesfd; fd_set readset; char buffer[1025];
FD_ZERO(&pipesfd); FD_SET(opipe[0], &pipesfd);
printf("Start the redirecting.\n");
// Timeout is 10 sec while (10 > time(0) - t) { readset = pipesfd;
// select() timeout is 1 sec. struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0;
int retval = select(1, &readset, 0, 0, &tv);
if (-1 == retval) { printf("Error: select()"); return -1; }
if (retval) { printf("Data is available now.\n"); // FD_ISSET(opipe[0], &readset) will be true.
int readed = read(opipe[0], buffer, 1024); if (0 < readed) { printf("===> Good select() result\n"); buffer[readed] = 0; printf("--> %s", buffer); } else { FD_CLR(opipe[0], &readset); printf("Close the input pipe."); } } else { printf("Error: No data within timeout.\n"); int readed = read(opipe[0], buffer, 1024); if (0 < readed) { printf("===> select() error ???\n"); buffer[readed] = 0; printf("--> %s", buffer); } } }
close(opipe[0]);
printf("Elapsed time: %d\n", time(0) - t);
return 0; }
Вот, что получается в результате: testuser@ws6~$ ./a.out Test 04-000-0000 testuser@ws6:~$ Start the redirecting. Error: No data within timeout. ===> select() error ??? --> run child process
PID TTY TIME CMD 15918 pts/0 00:00:00 bash 5963 pts/0 00:00:00 make 6770 pts/0 00:00:00 ps 6771 pts/0 00:00:00 test_04_000_000 Error: No data within timeout. Error: No data within timeout. Error: No data within timeout. Error: No data within timeout. Error: No data within timeout. Error: No data within timeout. Error: No data within timeout. Error: No data within timeout. Error: No data within timeout. Elapsed time: 10
Два момента: 1. Как я понимаю, если в pipe что-то есть, то программа должна пойти по ветке "Data is available now". Вместо этого программа пошла по ветке "Error: No data within timeout", но принудительное чтение из пайпа показывает, что там всё-таки есть данные... Почему? 2. После запуска программы a.out почти сразу вернулось управление в shell, как будто я выполнил "a.out &". Это побочный эффект вызова fork()? Что я здесь делаю неправильно?
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #11 : 10-05-2006 14:27 » |
|
В бекграунд уходить не должен. Быстрее всего он у тебя умирает, а ps продолжает работать. Вечерком проверю.
Контролировать дочек следует по SIGCHLD и за тем делать wait(), а иначе зомби будут.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #12 : 10-05-2006 16:12 » |
|
Ошибка первая: ты перепутал процессы - тот что с pid==0 - дочка. Ошибка вторая: первый параметер select() задает не число дескрипторов, а максимальный номер. Обычно fd+1 (opipe[0] + 1).
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Hooter
|
|
« Ответ #13 : 10-05-2006 18:37 » |
|
Ошибка первая: ты перепутал процессы - тот что с pid==0 - дочка.
Точно Спасибо, RXL. Завтра попытаюсь воспроизвести свою проблему на С.
|
|
|
Записан
|
|
|
|
PooH
Глобальный модератор
Offline
Пол:
... и можно без хлеба!
|
|
« Ответ #14 : 10-05-2006 19:26 » |
|
интересно, причем здесь пересборка...
|
|
|
Записан
|
Удачного всем кодинга! -=x[PooH]x=-
|
|
|
Hooter
|
|
« Ответ #15 : 11-05-2006 05:58 » |
|
Проверил. На С - работает, на Ада95 - нет. Именно при переборке. Буду разбираться. интересно, причем здесь пересборка...
Мне тоже очень интересно.
|
|
|
Записан
|
|
|
|
Hooter
|
|
« Ответ #16 : 11-05-2006 10:12 » |
|
Кажется, разобрался. В адском коде при вызове Select_File (аналог select) в цикле неправильно обрабатывлся набор дескрипторов File_Descriptor_Set (аналог fd_set). Уже на втором вызове Select_File результат был неправильный.
А с пересборкой это было связано так: дочерний модуль при первом запуске проводил некую инициализацию, что занимало какое-то время. Из-за этой начальной задержки дочернего модуля и возникал вышеописанный глюк. За этот отрезок времени набор дескрипторов уже успевал испортиться. Все последующие запуски дочернего модуля не требовали инициализации, поэтому первый вызов Select_file отрабатывал нормально.
RXL, еще раз спасибо за помощь.
|
|
|
Записан
|
|
|
|
rumax
Гость
|
|
« Ответ #17 : 30-11-2009 09:55 » |
|
Попробовал немного упростить код дл понимания всего процесса происходящего: #include <fcntl.h> #include <stdio.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h>
void childProcess() { int cnt = 10; while (cnt--) { printf("#Child# cnt = %d\n", cnt); sleep(1); } }
void parentProcess(int opipe) { int cnt = 10; char buffer[1025]; while (cnt--) { printf("#Parent# cnt = %d\n", cnt); printf("#Parent# ++read\n"); int readed = read(opipe, buffer, 1024); printf("#Parent# --read\n"); if (0 < readed) { printf("#Parent#===> Good read result\n"); buffer[readed] = 0; printf("#Parent#--> %s", buffer); } else { printf("#Parent# nothing to read\n"); }
sleep(1); } }
int main (int argc, char **argv) { pid_t pid; int opipe[2];
if (-1 == pipe(opipe)) { printf("Error: creating pipe"); return -1; }
pid = fork(); switch (pid) { case -1: printf("Error: fork()\n"); return -1; case 0: printf("redirect the standard output to the pipe for child\n"); // * 1 = stdout // * 2 = stderr dup2(opipe[1], 1); sleep(1); childProcess(); break; default: parentProcess(opipe[0]); break; } close(opipe[0]); close(opipe[1]);
return 0; }
и казалось бы - работает, да вот если закомментировать printf("redirect the standard output to the pipe for child\n"); то начинает подвисать на чтении. Кто в кусрсе что за проблема?
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #18 : 30-11-2009 17:59 » |
|
rumax, у меня к тебе два вопроса: знаешь, что такое отладчик и читаешь ли ты документацию? "Выводы" под кодом просто потрясающие!
1. dup() на существующий дескриптор сделать нельзя. 2. Неиспользуемый конец трубы в каждом процессе надо закрывать. 3. Процесс, порождающий другие процессы, должен перед своим завершением ожидать завершения своих потомков. См. wait4().
|
|
« Последнее редактирование: 30-11-2009 18:08 от Sel »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
|