C: dup2, труба и вилка не работают должным образом - PullRequest
7 голосов
/ 29 октября 2011

Я пытаюсь сделать простой форк -> выполнить другую программу -> поздороваться с этим дочерним процессом -> прочитать что-то -> напечатать то, что получил.

Программа, используемая в качестве дочернего элемента, просто ожидает любую строку ввода и выводит что-то на стандартный вывод типа «привет!»

Это моя программа "host" (она не работает):

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

#define IN 0
#define OUT 1
#define CHILD 0

main ()
{
  pid_t pid;
  int pipefd[2];
  FILE* output;
  char buf[256];

  pipe(pipefd);

  pid = fork();    
  if (pid == CHILD)
  {
    printf("child\n");
    dup2(pipefd[IN], IN);
    dup2(pipefd[OUT], OUT);
    execl("./test", "test", (char*) NULL);
  }
  else
  {
    sleep(1);
    printf("parent\n");
    write(pipefd[IN], "hello!", 10); // write message to the process    
    read(pipefd[OUT], buf, sizeof(buf));
    printf("received: %s\n", buf);
  }
}

Я понял:

child
[.. waits 1 second ..]
parent
received: 

Чего мне не хватает? Спасибо!

РЕДАКТИРОВАТЬ (test.c):

По запросу это дочерняя программа:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int getln(char line[])
{
  int nch = 0;
  int c;

  while((c = getchar()) != EOF)
    {
      if(c == '\n') break;
    line[nch] = c;
    nch++;
    }

 if(c == EOF && nch == 0) return EOF;
 return nch;
}

main()
{
  char line[20];

  getln(line);
  printf("hello there!", line);
  fflush(stdout);  
  return 0;
}

Ответы [ 6 ]

3 голосов
/ 29 октября 2011

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

Измените свой код на следующее:

main ()
{
  pid_t pid;
  int pipe_to_child[2];
  int pipe_from_child[2];
  FILE* output;
  char buf[256];

  pipe(pipe_to_child);
  pipe(pipe_from_child);

  pid = fork();    
  if (pid == CHILD)
  {
    printf("child\n");

    //child process not using these ends of the pipe, so close them
    close(pipe_to_child[1]);
    close(pipe_from_child[0]);

    dup2(pipe_to_child[0], fileno(stdin));
    dup2(pipe_from_child[1], fileno(stdout));

    execl("./test", "test", (char*) NULL);
  }
  else
  {
    sleep(1);
    printf("parent\n");
    write(pipe_to_child[1], "hello!\n", 10); // write message to the process    
    read(pipe_from_child[0], buf, sizeof(buf));
    printf("received: %s\n", buf);
  }
}
2 голосов
/ 29 октября 2011

Для этого вам понадобится две трубы: одна для стандартного ввода дочернего процесса и одна для стандартного вывода.Вы не можете повторно использовать два конца канала как два.

Кроме того, эта строка родительской программы

write(pipefd[IN], "hello!", 10); // write message to the process    

не записывает символ новой строки, поэтому getln в дочернем элементе будетникогда не возвращайся(Кроме того, «привет!» Имеет только шесть символов, но вы пишете десять.)

1 голос
/ 29 октября 2011

Другие говорят, что труба однонаправленная, о чем я и думал вначале. Но на самом деле это не то, что написано в моей справочной странице:

 A read from fildes[0] accesses the data written to fildes[1]
 on  a  first-in-first-out  (FIFO)  basis  and  a  read  from
 fildes[1] accesses the data written to fildes[0] also  on  a
 FIFO basis.

Однако это означает, что если родитель пишет в pipefd[0], тогда дочерний элемент должен читать из pipefd[1], поэтому вы связываете неправильную сторону канала с дочерними stdin и stdout.

Со страницы руководства кажется, что вы можете сделать это с помощью одной трубы. Но может быть более понятный код, чтобы использовать два.

Кажется, вы думаете о каждом элементе pipefd как о отдельной трубе, но это не так.

1 голос
/ 29 октября 2011

Похоже, у вас есть вход / выход назад для канала - pipefd[0] - конец чтения канала, поэтому запись в него (как это делает родитель) бессмысленна и не удастся. Точно так же pipefd[1] является концом записи, поэтому чтение с него (как это делает родитель) также не удастся. Вы должны ВСЕГДА проверять возвращаемые значения вызовов read и write, чтобы увидеть, нет ли у вас ошибок

1 голос
/ 29 октября 2011

Похоже, что ваши дескрипторы канала перепутаны. После вызова pipe(), pipefd[0] - это конец read канала, а pipefd[1] - конец write канала. Вы пишете в конец чтения, а читаете в конце записи.

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

1 голос
/ 29 октября 2011

Вы, вероятно, должны использовать wait или waitpid.

...