URL: https://www.opennet.ru/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 4206
[ Назад ]

Исходное сообщение
"странность с pipe()"

Отправлено programmator , 22-Апр-05 19:01 
Доброго времени суток!

Вот тут странноя штука вышла с пайпом - написал простенькое перенаправление вывода, причем работает :) но, прихорашивая текст, обнаружил странность.

В общем, создаю два пайпа и два потомка, у потомка stdin назначаю на pipe[0], stdout - на файл, который в потомке и создается. Вот эта функция работает после форка для потомка.

int run_child(char *output_file, int *fd)
{
    int out;
    int c;

    dup2(fd[0], 0);
    close(fd[0]);
    close(fd[1]);

    if(-1 != (out = open(output_file, O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE))){
    dup2(out, 1);
    close(out);

    execlp("cat", "cat", NULL);
    }

    exit(0);

    return 0;
}

Родитель для каждого потомка пишет сообщение в пайп, закрывает дескрипторы и ждет завершения потомков.

    for(i = 0; i < 2; i++){
    if(0 > (task[i].pid = fork())){
            printf("Fork error !\n");
            exit(2);
        }
    if(task[i].pid == 0)
            run_child(output_file[i], task[i].fd);
/************************* Здесь пишем ***********/
    close(task[i].fd[0]);
        write(task[i].fd[1], big_string, strlen(big_string));
    close(task[i].fd[1]);
/*************************************************/
    }

    for(i = 0; i < 2; i++)                        /*         waitpid(task[i].pid, &task[i].status, 0);

Все чудненько, строка уходит в соответствующий файл, Но если вынести запись в пайп из цикла(выделенный блок), то есть сначала создать всех потомков, а потом чего-то им послать, то начинаются странности: файлы либо нулевые, либо длиной 4096(зависит от размера строки-сообщения),
родитель намнртво встает на waitpid().

Программка махонькая, здесь она почти вся.
В принципе, она в таком вот виде работает, но я понимаю, что что-то упускаю. Вроде, по книжке делал :)

В общем, сам не пойму, а где еще найти таких гуру как здесь - не знаю.
Помогите, плиз, разобраться,


Содержание

Сообщения в этом обсуждении
"странность с pipe()"
Отправлено VLazarenko , 22-Апр-05 19:05 
Зря ты кусок обрезал. Так и не поймешь что, как и куда ты выносишь. Весь код в студию.

"странность с pipe()"
Отправлено programmator , 23-Апр-05 02:30 
>Зря ты кусок обрезал. Так и не поймешь что, как и куда
>ты выносишь. Весь код в студию.

Вот, пожалуйста. При пересылке почему-то теряются отступы, смотреть страшно:)
/********************Начало**************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>

char *output_file[] = {"out1", "out2"};

struct {
    int pid;
    int status;
    int fd[2];
} task[2];

#define BIG_SIZE    5000

char big_string[BIG_SIZE];

int run_child(char *output, int *fd)
{
    int out;
    int c;

    dup2(fd[0], 0);
    close(fd[0]);
    close(fd[1]);

    if(-1 != (out = open(output, O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE))){
    dup2(out, 1);
    close(out);

    execlp("cat", "cat", NULL);
    }

    exit(0);

    return 0;
}

main()
{
    int i;

    for(i = 0; i < BIG_SIZE; i++)
    big_string[i] = 'a';
    
    for(i = 0; i < 2; i++)
        if(pipe(task[i].fd) < 0){
        printf("Pipe error\n");
        exit(1);
        }
    for(i = 0; i < 2; i++){
    if(0 > (task[i].pid = fork())){
            printf("Fork error !\n");
            exit(2);
        }
    if(task[i].pid == 0)
            run_child(output_file[i], task[i].fd);
/**********Когда пишу здесь, то работает************************
    close(task[i].fd[0]);
        write(task[i].fd[1], big_string, BIG_SIZE);
    close(task[i].fd[1]);
*******************************************/
    }

/**************Если пишу здесь, то не работает********/
    for(i = 0; i < 2; i++){
    close(task[i].fd[0]);
        write(task[i].fd[1], big_string, BIG_SIZE);
    close(task[i].fd[1]);
    }
/******************************************/
    
    for(i = 0; i < 2; i++)
        waitpid(task[i].pid, &task[i].status, 0);

    return 0;
}
/****************Конец***********************/

$uname -a
Linux localhost.localdomain 2.4.3-20mdk #1 Sun Apr 15 23:03:10 CEST 2001 i686 unknown


"странность с pipe()"
Отправлено SergeiZz , 23-Апр-05 15:59 
>При пересылке почему-то теряются отступы, смотреть страшно:)
bash$ sed -e 's/\t/    /g' prog.c

"странность с pipe()"
Отправлено programmator , 24-Апр-05 10:25 
>>При пересылке почему-то теряются отступы, смотреть страшно:)
>bash$ sed -e 's/\t/    /g' prog.c

Cпасибо. Мне бы вот еще с пайпами разобраться :)


"странность с pipe()"
Отправлено SergeiZz , 25-Апр-05 14:48 
>>>При пересылке почему-то теряются отступы, смотреть страшно:)
>>bash$ sed -e 's/\t/    /g' prog.c
>
>Cпасибо. Мне бы вот еще с пайпами разобраться :)
Я просмотрел бегло (на большее нет времени). Вроде, много ошибок разных.
Во-первых, exec() заменяет на cat, а тот cat закроет файл или нет по умре?
Думаю, что закроет, если работает. Лучше, наверное, передать имя файла
cat, а не открывать его. Если это только тест, то стоит заменить exec() на
ручную запись.
Все write() написаны не аккуратно: нет проверок ошибок, write() не обязан
записывать все данные за один раз. 5000 скорее всего больше вместимости
канала (по POSIX -- не менее 512, но во всех системах поразному).
4096 -- это чай поди вместимость канала. Умозрительно (уточнять некогда),
дедлок получается когда родитель пишет в канал, переполняя его, а потомок
не успевает прочитать, освободив; но я не уверен. Проверить эту гипотезу
просто: нужно заменить 5000 на 512.

"странность с pipe()"
Отправлено programmator , 26-Апр-05 03:39 
>Во-первых, exec() заменяет на cat, а тот cat закроет файл или нет
>по умре?
>Думаю, что закроет, если работает. Лучше, наверное, передать имя файла
>cat, а не открывать его. Если это только тест, то стоит заменить
>exec() на
>ручную запись.

Мне надо было именно перенаправить stdout а не просто распечатать что-то.
Я так понимаю, свой stdout процесс по окончании работы закроет обязательно.

>Все write() написаны не аккуратно: нет проверок ошибок, write() не обязан
>записывать все данные за один раз. 5000 скорее всего больше вместимости
>канала (по POSIX -- не менее 512, но во всех системах поразному).

Неаккуратно, да. Просто это не профессиональное приложение, Так, демонстрашка возможностей UNIX :)

>4096 -- это чай поди вместимость канала. Умозрительно (уточнять некогда),

Да, так в мануалах и написано. Поэтому я писал больше 4096 байт.
Насчет write() и 512 байт... Так что же, выходит, каждый раз после отктытия файла в UNIX надо проверять, не пайп ли это? Обычно в файл пишут, не задумываясь о размерах записываемого(ну, в разумных пределах)
и проверяют, сколько записалось. А так выходит, что при записи более 512 байт можно нарваться на деадлок, если файл вдруг трубой оказался.


"странность с pipe()"
Отправлено SergeiZz , 26-Апр-05 15:57 
>Мне надо было именно перенаправить stdout а не просто распечатать что-то.
>Я так понимаю, свой stdout процесс по окончании работы закроет
>обязательно.
Надо бы проверить, но думаю, что проблем нет.

>>Все write() написаны не аккуратно: нет проверок ошибок, write() не обязан
>>записывать все данные за один раз. 5000 скорее всего больше вместимости
>>канала (по POSIX -- не менее 512, но во всех системах поразному).
>
>Неаккуратно, да. Просто это не профессиональное приложение, Так,
>демонстрашка возможностей UNIX :)
Тут write() -- самое интересное место, как я подозреваю. Потом, проверки
для read()/write() нужно привыкнуть писать на автомате (хорошо проверенная
рекомендация).

>Насчет write() и 512 байт... Так что же, выходит, каждый раз после
>отктытия файла в UNIX надо проверять, не пайп ли это? Обычно
>в файл пишут, не задумываясь о размерах записываемого(ну, в разумных пределах)
>
>и проверяют, сколько записалось. А так выходит, что при записи более 512
>байт можно нарваться на деадлок, если файл вдруг трубой оказался.
Труба и файл -- вещи принципиально разные. Это интерфейс намеренно сделан
одинаковым, семантика существенно разная. Если пишем в канал, то
подразумеваем, что на другом конце кто-то есть и он уже читает, ну разве
что он призапоздал и вот-вот начнёт читать (запоздание порядка времени
старта нового процесса). Но если тот читатель гикнулся, то кердык, так
сказать, -- наше предположение сильно не точно, ибо вместимость канала
очень маленькая, а read()/write() по умолчанию будут ждать у моря погоды.
512 байт не есть причина дедлока. Причина в том, что у трубы два конца, и
читателю нужен писатель (писателю не нужен читатель, вплоть до
переполнения канала). Если читатель висит в read(), то писатель висит в
waitpid() (похоже так тут и есть; это значит, что write() не сработал).
512 байт связано с другой проблемой: если в один и тот же канал пишут
двое, то как перемешаются их данные?

Если я правильно понял задачу, есть один писатель и два читателя,
которые должны сбрасывать данные в файлы. Читатели должны использовать
какие-то фильтры, читающие из stdin и пишущие в stdout. Вопрос: как
организовать надёжный обмен данными? Думается, достаточно проверять
успешность write() при записи в канал.

Кстати, я так и не удосужился пощупать этот код. Писатель виснет в
waitpid(). А что делают читатели (оба)?


"странность с pipe()"
Отправлено programmator , 26-Апр-05 17:59 
>>Неаккуратно, да. Просто это не профессиональное приложение, Так,
>>демонстрашка возможностей UNIX :)
>Тут write() -- самое интересное место, как я подозреваю. Потом, проверки
>для read()/write() нужно привыкнуть писать на автомате (хорошо проверенная
>рекомендация).
Да, конечно. В рабочей задаче я делаю всякие проверки, а тут - если все проверять, в экран не влазит :)

>Труба и файл -- вещи принципиально разные. Это интерфейс намеренно сделан
>одинаковым, семантика существенно разная. Если пишем в канал, то
>подразумеваем, что на другом конце кто-то есть и он уже читает, ну
>разве
>что он призапоздал и вот-вот начнёт читать (запоздание порядка времени
>старта нового процесса). Но если тот читатель гикнулся, то кердык, так
>сказать, -- наше предположение сильно не точно, ибо вместимость канала
>очень маленькая, а read()/write() по умолчанию будут ждать у моря погоды.
>512 байт не есть причина дедлока. Причина в том, что у трубы
>два конца, и
>читателю нужен писатель (писателю не нужен читатель, вплоть до
>переполнения канала). Если читатель висит в read(), то писатель висит в
>waitpid() (похоже так тут и есть; это значит, что write() не сработал).
>
>512 байт связано с другой проблемой: если в один и тот же
>канал пишут
>двое, то как перемешаются их данные?
Наверное, будет зависеть от того, как сбрасываются буфера(например, с помощью fflush()).

>Если я правильно понял задачу, есть один писатель и два читателя,
>которые должны сбрасывать данные в файлы. Читатели должны использовать
>какие-то фильтры, читающие из stdin и пишущие в stdout. Вопрос: как
>организовать надёжный обмен данными? Думается, достаточно проверять
>успешность write() при записи в канал.
Мне надо было запустить кучку процессов, а их stdout поместить в файлы.
В принципе, все бы могло сделаться через
system("taskNN < inputNN > outputNN");
но это должно запускать копию shell и быть более громоздким.
Поэтому я использовал трюки с форком и пайпом. Как я уже говорил, случайно я нашел сначала "рабочий" вариант и применил его. Но вопрос остался :)

>Кстати, я так и не удосужился пощупать этот код. Писатель виснет в
>
>waitpid(). А что делают читатели (оба)?
strace говорит, что они виснут на read(). Вроде как конца файла нет.

В общем, я нашел, где я был серьезно неправ. Я же создал две трубы, и для каждого потомка остается кроме рабочей трубы еще и труба, предназначенная для другого потомка. По хорошему, ее тоже надо закрывать.
Когда я стал закрывать другую трубу, все заработало.
Вообще говоря, у меня сложилось впечатление, что в разных юниксах
можно нарваться на разные неожиданности с тем же пайпом :)
Хотя я, конечно, понимаю, что эти мои проблемы - просто от недостатка соответствующего опыта.


"странность с pipe()"
Отправлено VLazarenko , 26-Апр-05 12:01 
Try this, this code works OK:

http://www.advancedlinuxprogramming.com/listings/chapter-5/p...
http://www.advancedlinuxprogramming.com/listings/chapter-5/d...

Good luck!


"странность с pipe()"
Отправлено Maxim Kuznetsov , 26-Апр-05 12:49 
как то недавно уже была некоторая тема с "advanced...",
и всё-равно ещё раз упомяну : ВСЕ примеры в книге (то есть ни один из них) не могут быть использованны без существенной доработки, они лишь демонстрируют общий принцип. Видимо и писались с такой целью, чтобы их нельзя было без риска использовать..


"странность с pipe()"
Отправлено SergeiZz , 26-Апр-05 16:10 
>как то недавно уже была некоторая тема с "advanced...",
>и всё-равно ещё раз упомяну : ВСЕ примеры в книге (то есть
>ни один из них) не могут быть использованны без существенной доработки,
>они лишь демонстрируют общий принцип. Видимо и писались с такой целью,
>чтобы их нельзя было без риска использовать..
Что указывает на низкое качество изложения.
По крайней мере, тонкие места обязательно должны быть выделены, если их
устранение оставляется читателю в качестве упражнения. Ещё и начало с основ
набора текста в Emacs подразумевает особый уровень изложения, так сказать,
для тех, кто хочет разобраться в вещах "само собой разумеющихся".
Но, для начинающих писать на C (а не на C++) для Linux книга эта весьма
полезна, по моему разумению.

"странность с pipe()"
Отправлено programmator , 26-Апр-05 18:16 
>Try this, this code works OK:
>
>http://www.advancedlinuxprogramming.com/listings/chapter-5/p...
>http://www.advancedlinuxprogramming.com/listings/chapter-5/d...
>
>Good luck!
Спасибо, полезный сайт.
В принципе, я вроде разобрался (ответ для SergeyZz).
Кстати, вопрос: нет ли где фри шелла на неинтеловской архитектуре? То есть, конечно, их много, но я не знаю бесплатного :) Скажем, для тестов мне удобно иногда пользоваться cyberspace.org - там OpenBSD с простой процедурой регистрации. Но хотелось бы неинтел. Привычка к интеловскому порядку байт иногда подводит :)

"Немогу разобраться с pipe и fork()"
Отправлено Mimik , 05-Сен-08 10:35 
Всем привет помогите плиз с моей трабл, немогу разобраться в чес дело
я пытаюсь перенаправить вывод данных с программы исполняемой в execlp
но в буфер однозначно ничего не выводит, никак не могу понять почему
вот исходник может есть соображения :

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
int main()
{
pid_t pid;
pid_t pido;
int pip[2];
char buffer[255] = "empty";
int status,died;
switch(fork())
{
case -1 :
      printf("%s","Error fork \n");
      return EXIT_FAILURE;
case 0:
    printf("%s","Run pid OMStip\n");
    pipe(pip);
    close(0);
    dup2(pip[1],1);
    close(pip[0]);
    close(0);
    pid = execlp("/local/mms-v20/lib/linux_v9/OMStip","OMStip","T75","-done","wan0",0);
    read(pid,buffer,255);
     close(pid);
     close(pip[0]);
     close(pip[1]);
    _exit(pip[1]);
}
printf("out : \n");
printf("%s\n",buffer);
close(0);
close(pip[0]);
close(pip[1]);
close(pid);
return EXIT_SUCCESS;  
}


"Немогу разобраться с pipe и fork()"
Отправлено phpcoder , 05-Сен-08 11:04 
>    pid = execlp("/local/mms-v20/lib/linux_v9/OMStip","OMStip","T75","-done","wan0",0);
>    read(pid,buffer,255);

Если мне не изменяет память, то ф-ции из семейства exec управление не возвращают, иными словами ваш read() даже не выполнится. Вам, видимо, нужно использовать popen().


"Немогу разобраться с pipe и fork()"
Отправлено Mimik , 05-Сен-08 13:37 
>>    pid = execlp("/local/mms-v20/lib/linux_v9/OMStip","OMStip","T75","-done","wan0",0);
>>    read(pid,buffer,255);
>
>Если мне не изменяет память, то ф-ции из семейства exec управление не
>возвращают, иными словами ваш read() даже не выполнится. Вам, видимо, нужно
>использовать popen().

FILE *pid = popen("/local/mms-v20/lib/linux_v9/OMStip T75 -done wan0","r");
read(pid,buffer,255);
получается надыть так?


"Немогу разобраться с pipe и fork()"
Отправлено phpcoder , 05-Сен-08 13:43 
>FILE *pid = popen("/local/mms-v20/lib/linux_v9/OMStip T75 -done wan0","r");
> read(pid,buffer,255);
>получается надыть так?

Не совсем так, потому что popen() возвращает FILE *, а read() ждёт int в качестве файлового дескриптора. Так что вам, по-видимому, нужно fread() использовать.


P.S. Почитайте маны)


"Немогу разобраться с pipe и fork()"
Отправлено Mimik , 05-Сен-08 13:48 
>>FILE *pid = popen("/local/mms-v20/lib/linux_v9/OMStip T75 -done wan0","r");
>> read(pid,buffer,255);
>>получается надыть так?
>
>Не совсем так, потому что popen() возвращает FILE *, а read() ждёт
>int в качестве файлового дескриптора. Так что вам, по-видимому, нужно fread()
>использовать.
>
>
>P.S. Почитайте маны)

Угу уже поитал уже разобрался всем спасиб =) терь тока надо закрыть и все


"Немогу разобраться с pipe и fork()"
Отправлено Mimik , 05-Сен-08 13:53 
>[оверквотинг удален]
>>
>>Не совсем так, потому что popen() возвращает FILE *, а read() ждёт
>>int в качестве файлового дескриптора. Так что вам, по-видимому, нужно fread()
>>использовать.
>>
>>
>>P.S. Почитайте маны)
>
>Угу уже поитал уже разобрался всем спасиб =) терь тока надо закрыть
>и все

хм, открываю получаю данные закрываю
коду получилось в 3 строчки =)
но теперь по завершению в консоли потерян фокус =(((


"Немогу разобраться с pipe и fork()"
Отправлено Mimik , 05-Сен-08 14:24 
>[оверквотинг удален]
>>>
>>>
>>>P.S. Почитайте маны)
>>
>>Угу уже поитал уже разобрался всем спасиб =) терь тока надо закрыть
>>и все
>
>хм, открываю получаю данные закрываю
>коду получилось в 3 строчки =)
>но теперь по завершению в консоли потерян фокус =(((

делаю так :

int main()
{
char buf[256] = "null";
FILE *oms = popen("patch/OMStip T75 -done wan0","r");

if(!oms)
{
  printf("Error read file\n");
  return EXIT_FAILURE;
}
else
{
  printf("Read run\n");
  fread(buf,1,256,oms);
  printf("%s",buf);
  fclose(oms);
}


}

но приложение отрабатывет со второго раза о_О, и после исполнения теряется фокус у консоли
в нее ничего нельзя ввести О_О как это побороть?


"Немогу разобраться с pipe и fork()"
Отправлено phpcoder , 05-Сен-08 14:50 
Где хедеры stdio.h. и stdlib.h ?

>int main()

В Си если ф-ция не принимает параметров, лучше указывать void, т.е. в вашем случае правильнее будет

int main(void)

>{
>char buf[256] = "null";
>FILE *oms = popen("patch/OMStip T75 -done wan0","r");
>
>if(!oms)
> {
>  printf("Error read file\n");

Сообщение об ошибках всё же лучше направлять в stderr. Ну и оно должно быть более читабельным, чем в вашем случае.

>  return EXIT_FAILURE;
> }
> else
> {
>  printf("Read run\n");
>  fread(buf,1,256,oms);

У вас размер буфера 256, как вы собрались туда прочитать 256 символов? А как же нулевой символ? Вы так можете получить ошибку off-by-one и выход за границы массива.

>  printf("%s",buf);
>  fclose(oms);

fclose() используется для закрытия файловых дескрипторов, открытых с помощью fopen(). В вашем случае нужно pclose()

> }
>

Здесь должен быть оператор return EXIT_SUCCESS;

>}
>
>но приложение отрабатывет со второго раза о_О, и после исполнения теряется фокус
>у консоли
>в нее ничего нельзя ввести О_О как это побороть?

У меня ваш пример (после некоторых правок) отрабатывает с первого раза и никакой фокус не теряется.


"Немогу разобраться с pipe и fork()"
Отправлено Mimik , 12-Ноя-12 17:08 
прошло 4 года, читаю и смеюсь сам над собой =) вот глупый был =)))