Модель программирования процесса с использованием Linux pipe () - PullRequest
2 голосов
/ 02 августа 2011

С http://pubs.opengroup.org/onlinepubs/009604599/functions/pipe.html:

Функция pipe () должна создать канал и поместить два файла дескрипторы, по одному в аргументы fildes [0] и fildes [1], которые обратитесь к описанию открытого файла для чтения и записи конца труба.

Есть пример, когда родитель записывает данные для своего потомка:

int fildes[2];
const int BSIZE = 100;
char buf[BSIZE];
ssize_t nbytes;
int status;


status = pipe(fildes);
if (status == -1 ) {
    /* an error occurred */
    ...
}


switch (fork()) {
case -1: /* Handle error */
    break;


case 0:  /* Child - reads from pipe */
    close(fildes[1]);                       /* Write end is unused */
    nbytes = read(fildes[0], buf, BSIZE);   /* Get data from pipe */
    /* At this point, a further read would see end of file ... */
    close(fildes[0]);                       /* Finished with pipe */
    exit(EXIT_SUCCESS);


default:  /* Parent - writes to pipe */
    close(fildes[0]);                       /* Read end is unused */
    write(fildes[1], "Hello world\n", 12);  /* Write data on pipe */
    close(fildes[1]);                       /* Child will see EOF */
    exit(EXIT_SUCCESS);
}
  1. Мне было интересно, как родитель, использующий канал для связи со своим дочерним элементом, может убедиться, что не произойдет, что дочерний элемент выполняет read () до того, как родительский элемент запустит write, а дочерний элемент закончить чтение до того, как родитель закончит писать?

  2. Мне было интересно, можно ли использовать канал, созданный в родительском элементе, для двустороннее общение между родителем и ребенком или только один путь от родитель к ребенку не по другому?

    Если ребенок может отправлять данные родителю через канал, что будет программа выглядит так?

  3. Является ли труба похожей на настоящую трубу с двумя концами. fildes [0] и fildes [1] используются для представления двух концов соответственно?

    Если канал двусторонний, что означает чтение и конец записи в отношении, то есть одного читателя (пишите), родителя или ребенка?

Спасибо и всего наилучшего!

Ответы [ 2 ]

4 голосов
/ 02 августа 2011
  1. Если вы не укажете O_NONBLOCK, read будет блокироваться и ждать, пока данные не станут доступны. Так что это не имеет значения; если данные еще не доступны, они будут ждать их.
  2. Нет, трубы не дуплексные. Если вы хотите двунаправленную связь, вы должны создать два канала (что даст вам всего четыре дескриптора файла). fildes[0] всегда означает конец чтения (выходной), а fildes[1] - конец записи (входной). Таким образом, вы настраиваете один канал точно так же, как вы это сделали (где родитель получает конец записи, а потомок получает конец чтения, чтобы родитель мог отправить дочернему элементу), а другой канал настраивается противоположным образом ( вы позволяете родителю сохранить конец чтения и дать ребенку конец письма).
  3. fildes[0] и fildes[1] действительно представляют собой концы трубы, но они не являются взаимозаменяемыми. Один используется для ввода данных «в канал», а другой - для вывода данных «из» канала.

Если вам нужно больше разъяснений, не стесняйтесь спрашивать; Я понимаю, что это может сбить с толку новичков.

Обновление для ответа на вопросы в комментарии:

  1. Любой процесс может вызвать pipe(), и его подпроцессы будут наследовать файловые дескрипторы, которые он получает (если вы не закроете их). То есть они выживают через несколько fork() вызовов. Таким образом, вы можете написать программу, которая будет передавать файловые дескрипторы внукам, правнукам и т. Д., Если хотите.
  2. Конечно, два родственных процесса могут общаться следующим образом. Родитель вызывает pipe(), чтобы создать пару (или две пары, если вы хотите двунаправленную связь), затем дважды разветвляется. После этого один ребенок может использовать fildes[0], а другой ребенок - fildes[1]. Родитель не должен использовать ни один файловый дескриптор. Вуаля! Общение детей с общим родителем. Примечательно, насколько мощна эта простая (хотя и потенциально неинтуитивная) функция. Это выглядит примерно так, как показано ниже.
  3. Изображение fork() как клонирующая машина. Прежде чем войти в клонирующую машину, вы используете pipe(), чтобы создать пару подходящих раций, которые используют особую секретную частоту. Можно только передавать; другой может только получить. Когда вы используете клонирующую машину, у вас появляются две копии (оригинал, то есть родитель, и клон, то есть ребенок). В каждой копии теперь есть идентичная пара подходящих раций. Если оригинал уничтожает свое «принимающее» устройство, а клон уничтожает его «передающее» устройство, то оригинал может общаться с клоном с помощью раций (но не наоборот).

    Но вы видите, ключевая идея здесь не в том, что pipe() соединяет два процесса (хотя это его основное использование), а в том, что это что-то, что дублируется во время fork(). Вы можете повторно войти в клонирующую машину много раз и получить множество раций, поэтому то, что определяет «кто с кем разговаривает» (какие процессы связаны между собой каналом), не является специфическим аспектом функции клонирующей машины или рации.

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

Пример родственных процессов, говорящих с трубой (однонаправленный):

int fildes[2];
int status;

status = pipe(fildes);
if (status == -1) { ... }

// first fork
switch (fork()) {
case -1:
    // handle error
    break;

case 0:
    // child 1 (with the writing end -- close the writing end)
    close(fildes[0]);
    // your logic for child 1 here, using fildes[1] to write
    return;

default:
    // not child 1: close the writing end
    close(fildes[1]);
    break;
}

// second fork
switch (fork()) {
case -1:
    // handle error
    break;

case 0:
    // child 2 (with the reading end)
    // your logic for child 2 here, using fildes[0] to read
    return;
}

// still in the parent
// logic for the common parent here, after forking both children
1 голос
/ 02 августа 2011
  1. Если дочерний элемент запускает read(), он будет блокироваться до тех пор, пока не будут доступны данные, если для дескриптора не установлено значение , а блокировка не установлена.Если дочерний элемент заканчивает чтение и закрывает канал, родительский объект получит сигнал SIGPIPE и write() прервет работу.

  2. Канал обычно является однонаправленной связью.Существуют (слухи) реализации двунаправленных каналов, но я никогда не сталкивался с одним из них.

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

Возможно man 7 pipe вам тоже поможет.

...