Модераторы: xvr

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Перенаправление stdout из fork/exec, как "подтолкнуть" данные? 
V
    Опции темы
null56
Дата 14.4.2011, 22:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



Собственно похожие темы уже освещались: http://forum.vingrad.ru/topic-278411.html
Напомню:
есть две программы: родительская и вызываемая (дочерняя)
- родительская создает некоторый канал, посредством pipe
- далее запускает fork
- в fork перенаправляет дескриптор stdout в пишущий конец, ранее созданной, трубы
- далее проиходит exec внешней программы
- в родительском по идее можно теперь читать из свободного конца трубы, то что пишет вызываемая программа
ЭТО ВСЕ РАБОТАЕТ за одним НО
Вот код
Код

// создадим пайп
int pipe_fd[2];
pipe(pipe_fd);

// сделаем ответвление
pid = fork();

// в дочернем перенаправим stdout в новый дескриптор,
// закроем родительский конец
// сделаем exec внешней программы
dup2(pipe_fd[1], 1));
close(pipe_fd[0]);
execv("./a.out", 0);

//в родительской попробуем почитать
a = read(pipe_fd[0], my_buff, 255);


код приложения, которое мы запускаем
Код

   sleep(30);
   printf("hello world\n");
   //fflush(stdout); // вот тут момент !!!!!!!!!!!!!!!!!
   sleep(30);

довольно примитивное приложение. таймеры сделаны для того, чтобы приложение не успело закрыться до того, как в родительском процессе не дошли до функции read.

Собственно вопрос: почему в канале родительского приложения данные от дочернего появляются только после завершения последнего? или же необходимо использовать сброс буфера fflush, тогда данные приходят в тот же момент?

если кто - то знает ответ, подскажите пожалуйста как можно решить этот момент, если конечно возможно...
неужели такая дикая буферизация?

надеюсь вопрос понятен, заранее благодарен за помощь

Это сообщение отредактировал(а) null56 - 15.4.2011, 13:25
PM MAIL   Вверх
null56
Дата 15.4.2011, 00:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



при чем, проблема не в fork, из форка, если отправлять сообщения в новоиспеченный пайп, все доходит моментально... видимо под вопросом сам exec
системный вызов не этот отвечает за все семейство exec?
http://tomoyo.sourceforge.jp/cgi-bin/lxr/s...fs/exec.c#L1382

PM MAIL   Вверх
svlary
Дата 15.4.2011, 05:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 207
Регистрация: 8.9.2009

Репутация: 4
Всего: 4



Цитата(null56 @  14.4.2011,  22:10 Найти цитируемый пост)
// создадим пайп
int pipe_fd;
pipe(pipe_fd);


Ну для начала, стоит, наверное, обратить внимание на то, что в man pipe написано :

Код

int pipe(int pipefd[2]);


pipe_fd - МАССИВ из двух элементов
PM MAIL   Вверх
MAKCim
Дата 15.4.2011, 08:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



и проверять надобно pid


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
null56
Дата 15.4.2011, 13:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



svlary, опечатка тут, в программе все верно
MAKCim, на что его проверять? да его номер соответствует запускаемой программе

для достоверности в правильности моего кода, есть glib функция, работающая аналогично, только использующая clone, эффект тот же
http://forum.vingrad.ru/forum/topic-327012.html

ЗЫ: поправил  pipe[2] в старте темы

Это сообщение отредактировал(а) null56 - 15.4.2011, 13:26
PM MAIL   Вверх
null56
Дата 15.4.2011, 14:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



с stderr все нормально, работает как надо, видимо по умолчанию там стоит построчный вывод

Добавлено через 7 минут и 18 секунд
при чем попытка сделать stdout 
Код

void setlinebuf(FILE *stream);

ни к чему не приводит, видимо execv сбрасывает назад
PM MAIL   Вверх
MAKCim
Дата 15.4.2011, 16:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



Цитата(null56 @  15.4.2011,  13:24 Найти цитируемый пост)
на что его проверять? да его номер соответствует запускаемой программе

на дочерний/родительский процесс


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
null56
Дата 15.4.2011, 17:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



MAKCim, в коде подобных ошибок нет... все проверяется... могу код предоставить, не только у меня такая вещь происходит, ведь с glib тоже подобный эффект с stdout. тут в другом проблема и она происходит после вызова exec*, судя по всему за счет кеширования данных ядром в буферах... при том в fork я могу сделать 
что - то типа
Код

setlinebuf(stdout);

и перенаправление данных записанных в конец пайпа нормально без блокировки перенаправляется родительскому процессу, но после вызова exec видимо буферы опять выставляются в первоначальное состояние, после завершения процесса, ядро сбрасывает данные наконец в трубу.
с stderr ситуация прямопротивоположная. если я не ошибаюсь, там по умолчанию выставлено кеширование по линиям, поэтому данные приходят в другой конец пайпа без задержек
проверено на коде
Код

   // фрагмент из дочернего приложения
   fprintf(stdout, "from stdout\n");
   fprintf(stderr, "hello world\n");

данные из stderr доходят сразу после вызова, а вот stdout только после завершения программы... или, видимо, если переполнится буфер.
но на буфер, как было сказано выше я повлиять не могу. в общем система думаю примерна такая, пока нет времени разбираться с внутренностями

Добавлено @ 18:00
чтобы не гадали, упрощенный вариант кода (на синтаксические ошибки не обращайте внимания ибо копипастил с правкой на лету, дабы убрать лишнее)
если есть ошибки, буду рад услышать
Код


//+ macro

#define CLOSE_PIPE(p) \
    close(p[0]); \
    close(p[1]);

//- macro

//+ prototype

/**
 * action of parent process
 */
int do_parent_process(int child_pid);

//- prototype

//+ inline

/**
 * create pipe if need
 *
 * @return 0 - success; -1 - fail
 */
inline int create_pipe(int * fd, int * pipe_fd, int flags, int assign_idx)
{
    if (fd)
    {
        if (pipe(pipe_fd))
            {   return -1;   }
        *fd = pipe_fd[assign_idx];
    }
    return 0;
}

//- inline

/**
 * execute external application, bind std channels to pipe
 *
 * @param argv - filename for execute and args
 * @param std_in - if !null than return stdin of child process
 * @param std_out - if !null than return stdout of child process
 * @param out_flags - flags for child stdout
 * @param std_err - if !null than return stderr of child process
 * @param err_flags - flags for child stderr
 *
 * @return process id of -1 if fail
 */
int posix_execute(const char ** argv, int * std_in, int * std_out, int * std_err)
{
    int pid = -1;
    int std_in_pipe[2] = {0, 0}, std_out_pipe[2] = {0, 0}, std_err_pipe[2] = {0, 0};
    if (!create_pipe(std_in, std_in_pipe, 0, 1) &&
        !create_pipe(std_out, std_out_pipe, out_flags, 0) &&
        !create_pipe(std_err, std_err_pipe, err_flags, 0))
    {
        pid = fork();
        if (pid == 0)
        {
            if (std_in)
                {   dup2(std_in_pipe[0], STDIN_FILENO); close(std_in_pipe[1]);   }
            if (std_out)
                {   dup2(std_out_pipe[1], STDOUT_FILENO); close(std_out_pipe[0]); }
            if (std_err)
                {   dup2(std_err_pipe[1], STDERR_FILENO); close(std_err_pipe[0]);   }
            execv(*argv, 0);
        }
        else if (pid == -1)
        {    // fail
            CLOSE_PIPE(std_in_pipe);
            CLOSE_PIPE(std_out_pipe);
            CLOSE_PIPE(std_err_pipe);
        }
        else
        {    // parent
            // читаю выше, где идет вызов
        }
    }
    return pid;
}



Это сообщение отредактировал(а) null56 - 15.4.2011, 18:01
PM MAIL   Вверх
MAKCim
Дата 15.4.2011, 18:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



null56
вместо printf попробуй сделать write тут
Код

sleep(30);
   printf("hello world\n");
   //fflush(stdout); // вот тут момент !!!!!!!!!!!!!!!!!
   sleep(30

или перед printf
Код

setvbuf(stdout, NULL, _IONBF, 0);



--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
null56
Дата 15.4.2011, 18:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



Цитата

NOTES
       The  stream  stderr is unbuffered.  The stream stdout is line-buffered when it points to a terminal. 


Добавлено @ 18:24
MAKCim, да, знаю эту функцию, пробовал ее использовать перед exec, не катило.
согласен с тобой, но цель использовать не мое приложение, а чужое, не патчить же его...
но я уже забил на эту идею, ибо приложение умеет  кушать файл, я ему лучше его скормлю и проанализирую код возврата
если интересно: приложение nsupdate

Добавлено @ 18:25
в общем проблему, а она заключалась в вопросе "почему", думаю я разрешил для себя и видимо сделать на родительской стороне ничего поделать нельзя в юзермод

Добавлено через 10 минут и 18 секунд
спасибо большое всем за участие

Это сообщение отредактировал(а) null56 - 15.4.2011, 18:26
PM MAIL   Вверх
svlary
Дата 18.4.2011, 05:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 207
Регистрация: 8.9.2009

Репутация: 4
Всего: 4



Цитата(null56 @  15.4.2011,  18:16 Найти цитируемый пост)
видимо сделать на родительской стороне ничего поделать 

Странно все это... Именно сейчас я разрабатываю систему, в которой 5 процессов обмениваются информацией через 6 пайпов. И - никаких проблем! Все сообщения отправляются и доставляются практически мгновенно.  Единственная разница, которую я увидел : в моей программе обмен сообщениями осуществляется посредством write(...) -> read(...), а не посредством printf(..) -> read(...). Кстати говоря, мне кажется. что если уж на одном конце используется printf, то на другом конце надо использовать skanf(...);

  Кстати говоря, очень похоже, что :

Код

//в родительской попробуем почитать
a = read(pipe_fd[0], my_buff, 255);


просто-напросто висит, ожидая получения 255 байт. И ожидание завершается только в тот, момент, когда закрывается пайт - т.е. при  завершении приложения на "том" конце.  Посмотрите внимательно, в каком режиме открывается у Вас  pipe_fd[0]...
PM MAIL   Вверх
leniviy
Дата 18.4.2011, 14:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 552
Регистрация: 8.2.2003
Где: Спб

Репутация: нет
Всего: 5



офф: Че-то у меня execv("/bin/ls", 0); не работает, а execl("/bin/ls", 0); работает

Добавлено через 12 минут и 44 секунды
После строчки надо добавить, потому что если exec не сработает, или дочерний процесс ничего не запишет, родительский процесс зависнет
Код

//в родительской попробуем почитать
close(pipe_fd[1]);

это единственный close(), который тут нужен.
PM MAIL   Вверх
maint
Дата 18.4.2011, 15:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 77
Регистрация: 12.5.2008

Репутация: нет
Всего: 2



Цитата

Че-то у меня execv("/bin/ls", 0); не работает, а execl("/bin/ls", 0); работает

очевидно потому, что у execv второй аргумент char **. 
PM   Вверх
null56
Дата 19.4.2011, 23:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



Цитата(svlary @  18.4.2011,  05:44 Найти цитируемый пост)
Единственная разница, которую я увидел : в моей программе обмен сообщениями осуществляется посредством write(...) -> read(...), а не посредством printf(..) -> read(...). Кстати говоря, мне кажется. что если уж на одном конце используется printf, то на другом конце надо использовать skanf(...);

все верно, write не буферизуется, обратите внимание на то, что я написал: ПРОГРАММА, КОТОРАЯ ВЫВОДИТ - НЕ МОЯ!!! а так да, вы правы, так и нужно делать, чтобы получать немедленно  smile

Добавлено через 4 минуты и 50 секунд
Цитата(svlary @  18.4.2011,  05:44 Найти цитируемый пост)
Посмотрите внимательно, в каком режиме открывается у Вас  pipe_fd[0]... 

я пытался передать и прочитать один байт, одно и тоже
PM MAIL   Вверх
svlary
Дата 20.4.2011, 05:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 207
Регистрация: 8.9.2009

Репутация: 4
Всего: 4



Цитата(null56 @  19.4.2011,  23:26 Найти цитируемый пост)
write не буферизуется


Читаем man 2 write :

Код

ЗАМЕЧАНИЯ
       Успешный возврат из вызова write() не даёт никаких гарантий, что данные сохранены  на  диске.
       Фактически,  в  некоторых  ошибочных  реализациях  даже  нет  гарантии,  что  для данных было
       зарезервировано место. Единственный способ получить гарантированную запись — вызвать fsync(2)
       после записи всех данных.



   В том смысле, что очень даже БУФЕРИЗУЕТСЯ!  
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Программирование под Unix/Linux"
xvr
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • Не забывайте пользоваться кнопкой "Код".
  • Вопросы мобильной разработки тут
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к разделу форума. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, xvr.

 
 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Программирование под Unix/Linux | Следующая тема »


 




[ Время генерации скрипта: 0.1341 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.