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

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

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

« : 10-05-2006 06:45 » 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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