Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Проблема перехвата потоков вывода дочерних процессов  (Прочитано 17393 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« : 10-05-2006 06:45 » 

Ситуация следующая.

Есть головной процесс, который запускает дочерние процессы и перехватывает потоки вывода (out и err) этих дочерних процессов. Головной процесс с помощью select() ожидает появления каких-либо данных в потоках вывода дочерних процессов, читает эти данные и выводит их в файл.

Проблема в том, что данные в потоках вывода не всегда перехватываются головным процессом.

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

То есть такое впечатление, что иногда данные просто не поступают от дочернего процесса.

Я смог локализовать условия, когда возникает такая ситуация: если перед запуском выполнить пересборку дочерней программы - вывод не перехватывается. Если не пересобирать дочернюю программу, а запускать уже существующую - всё нормально. Но мне это ни о чём не говорит Жаль Под пересборкой программы я подразумеваю просто пересборку программы без каких-либо ёё изменений.

Кто сталкивался с подобным поведением? В чем может быть проблема?
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 10-05-2006 06:58 » 

Hooter, какие ф-ии используются для вывода? Прямой write() или потоковые? Может перед завершением сделать сброс буферов?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.

Хз, я не очень просто не очень во всё это верю, во всякие там сатурны и прочую поебень.
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 10-05-2006 07:07 » 

В конечном счете вызывается write. Делал вызов flush - то же самое.
« Последнее редактирование: 10-05-2006 07:10 от Hooter » Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 10-05-2006 07:19 » 

Сдается мне, в программе ошибка. Всегда, когда у меня происходило что-то подобное, оказывалось, что где-то маленькая ошибка, типа перепутанных переменных. Попробуй простые тесты написать (только не copy-paste) для клиента и сервера. Тест-клиент должен выполнить один write() и завершиться, а тест-сервер должен запустить и втупую принять из потока.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.

Хз, я не очень просто не очень во всё это верю, во всякие там сатурны и прочую поебень.
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 10-05-2006 07:20 » 

Я прошу прощения, в конечном счете вызывается fwrite, а не write.
Записан
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 10-05-2006 07:24 » 

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

То есть, если пересобирать дочерний модуль - потоки не перехватываются. Если же запускать после этого программу без пересборки - то все последующие запуски показывают, что все нормально...

Может быть это как-то связано с инициализацией?
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 10-05-2006 07:30 » 

Hooter, а главная программа у тебя постоянно загружена? После пересборки ты ее останавливаешь?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.

Хз, я не очень просто не очень во всё это верю, во всякие там сатурны и прочую поебень.
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 10-05-2006 08:32 » 

Останавливаю с завершением всех дочерних процессов. То есть каждый раз всё запускается по новой.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #8 : 10-05-2006 09:05 » 

Т.е. и нормальные (которые не работают) запуски то же целиком с остановкой?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.

Хз, я не очень просто не очень во всё это верю, во всякие там сатурны и прочую поебень.
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 10-05-2006 10:46 » 

Да, именно так.
Записан
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #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
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #11 : 10-05-2006 14:27 » 

В бекграунд уходить не должен. Быстрее всего он у тебя умирает, а ps продолжает работать.
Вечерком проверю.

Контролировать дочек следует по SIGCHLD и за тем делать wait(), а иначе зомби будут.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.

Хз, я не очень просто не очень во всё это верю, во всякие там сатурны и прочую поебень.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #12 : 10-05-2006 16:12 » 

Ошибка первая: ты перепутал процессы - тот что с pid==0 - дочка.
Ошибка вторая: первый параметер select() задает не число дескрипторов, а максимальный номер. Обычно fd+1 (opipe[0] + 1).
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.

Хз, я не очень просто не очень во всё это верю, во всякие там сатурны и прочую поебень.
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #13 : 10-05-2006 18:37 » 

Ошибка первая: ты перепутал процессы - тот что с pid==0 - дочка.
Точно Улыбаюсь Спасибо, RXL. Завтра попытаюсь воспроизвести свою проблему на С.
Записан
PooH
Глобальный модератор

ru
Offline Offline
Пол: Мужской
... и можно без хлеба!


« Ответ #14 : 10-05-2006 19:26 » 

интересно, причем здесь пересборка...
Записан

Удачного всем кодинга! -=x[PooH]x=-
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 11-05-2006 05:58 » 

Проверил. На С - работает, на Ада95 - нет. Именно при переборке. Буду разбираться.

интересно, причем здесь пересборка...
Мне тоже очень интересно.
Записан
Hooter
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #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
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #18 : 30-11-2009 17:59 » 

rumax, у меня к тебе два вопроса: знаешь, что такое отладчик и читаешь ли ты документацию? "Выводы" под кодом просто потрясающие!

1. dup() на существующий дескриптор сделать нельзя.
2. Неиспользуемый конец трубы в каждом процессе надо закрывать.
3. Процесс, порождающий другие процессы, должен перед своим завершением ожидать завершения своих потомков. См. wait4().
« Последнее редактирование: 30-11-2009 18:08 от Sel » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.

Хз, я не очень просто не очень во всё это верю, во всякие там сатурны и прочую поебень.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines