C - прохождение трубы через execve - PullRequest
1 голос
/ 10 сентября 2011

Я работаю над проектом для школы, и я не уверен, возможно ли даже то, как я пытаюсь его решить. Проект включает в себя создание программы, разветвление 2 детей, которые затем должны заменить свои pid другими программами, и 2 ребенка, разговаривающих по каналу, с помощью read () и write ().

У меня вопрос с использованием execve и передачей канала этому ребенку. То, что у меня сейчас есть, это:

Родительская программа - разветвление и выполнение дочернего вызова execve:

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BUF_SIZE 16

/*  2 cmd line arguments
    1. file already in system (to be copied)
    2. file to be created (the copy)
create pipe (for communication) + 2 child processes
    first child replace pid with reader
    second child with writer
    wait for both children to terminate before exiting
*/

int main(int argc, char* argv[]){

//making the pipe
int pfd[2];
pipe(pfd);

int num_dead;

//forking
pid_t reader_child_pid;
pid_t writer_child_pid;
pid_t child_pid;

//args for each fork
char *args_1[] = {argv[1], (char *) 0};
char *args_2[] = {argv[2], (char *) 0};

switch(writer_child_pid = fork()) {
    case -1:
        perror("fork failed");
        return 1;
    case 0:
        close(pfd[1]);
        dup2(pfd[0], 0);
        execve("./writer", args_2, NULL);
        perror("execve failed");
        break;
    default:
        switch(reader_child_pid = fork()) {
            case -1:
                perror("2nd fork failed");
                return 1;
            case 0:
                close(pfd[0]);
                dup2(pfd[1], 1);
                execve("./reader", args_1, NULL);
                perror("execve failed");
                break;
            default:
                //close(pfd[0]);
                //close(pfd[1]);
                break;
        }
}

num_dead = 0;

for(;;) {
    if((child_pid=wait(NULL)) == -1){
        if(errno == ECHILD) {
            printf("NO MORE CHILDREN");
            exit(0);
        } else {
            perror("wait error");
            exit(1);
        }
    }
    ++num_dead;
    printf("wait() returned 1 child");
}
}

Попытка использовать dup2 для перенаправления на stdin и stdout. Затем в детях я пытаюсь читать и писать на стандартный вывод и стандартный ввод, вот так:

Ребенок - чтение данных со стандартного ввода

#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#define BUF_SIZE 16
int main(int argc, char* argv[]){
printf("\n\nWRITER\n");

    /* ready to process info
    read the file, write it to pipe */
int wri_inFile = open(argv[0], O_WRONLY | O_CREAT | O_TRUNC | S_IRUSR | S_IWUSR);

char buf[BUF_SIZE];
int read_test;
for(;;) {
    read_test = read(0, buf, BUF_SIZE);
    if(read_test == 0) //eof
        break;
    write(wri_inFile, buf, BUF_SIZE);
}

close(wri_inFile);
exit(0);
}

Я использую dup согласно вашим предложениям, но я не уверен, что делаю это, получая доступ к stdin и stdout, как и должно быть.

- Вот другой ребенок, только для справки

#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#define BUF_SIZE 16

int main(int argc, char* argv[]){

printf("\n\nREADER\n");

/* ready to process info
    read the file, write it to pipe */
printf("FILE::%s", argv[0]);
int inFile = open(argv[0], O_RDONLY);

char buf[BUF_SIZE];
int read_test;
for(;;) {
    read_test = read(inFile, buf, BUF_SIZE);
    if(read_test == 0) //eof
        break;
    write(1, buf, BUF_SIZE);
}
close(argv[0][1]);
close(inFile);
printf("\nDONE WITH READER\n");
exit(0);
}

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

** все то же самое происходит

Я не ищу, чтобы вы решили проблему для меня, я просто застрял и не знаю, делаю ли я что-то крайне неправильное. Еще раз спасибо за любую помощь.

Я активно ищу примеры для работы, но я не могу найти ни одного, который бы показывал код от детей, с которыми они работают, и где моя проблема, похоже, сейчас.

Ответы [ 2 ]

4 голосов
/ 10 сентября 2011

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

После более тщательного чтения двух других программ я вижу, что программа чтениячитает файл, который ему дан, и записывает это содержимое в канал, а программа записи записывает файл, который ему дается, с данными, которые она читает из канала.

Этот код о минимальных изменениях, которые будут работать:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

/*  2 cmd line arguments
    1. file already in system (to be copied)
    2. file to be created (the copy)
create pipe (for communication) + 2 child processes
    first child replace pid with reader
    second child with writer
    wait for both children to terminate before exiting
*/

int main(int argc, char* argv[])
{
    //making the pipe
    int pfd[2];
    pipe(pfd);      // Error check omitted!
    int num_dead;

    //forking
    pid_t reader_child_pid;
    pid_t writer_child_pid;
    pid_t child_pid;

    if (argc != 3)
    {
        fprintf(stderr, "Usage: %s infile outfile\n", argv[0]);
        return 1;
    }

    //args for each fork
    char *args_1[] = { "reader", argv[1], (char *) 0 };
    char *args_2[] = { "writer", argv[2], (char *) 0 };

    switch (writer_child_pid = fork()) {
        case -1:
            perror("fork failed");
            return 1;

        case 0:
            // writer reads from standard input (pipe) and writes to named file
            dup2(pfd[0], 0);  // Error check omitted
            close(pfd[0]);
            close(pfd[1]);
            execve("./writer", args_2, NULL);
            perror("execve failed");
            return 1;

        default:
            switch (reader_child_pid = fork()) {
            case -1:
                perror("2nd fork failed");
                return 1;

            case 0:
                //reader reads from the named file and writes to the pipe
                dup2(pfd[1], 1);
                close(pfd[0]);
                close(pfd[1]);
                execve("./reader", args_1, NULL);
                perror("execve failed");
                return 1;

            default:
                // The parent closes both ends of the pipe
                close(pfd[0]);
                close(pfd[1]);
                break;
        }
    }

    num_dead = 0;

    for(;;) {
        if ((child_pid = wait(NULL)) == -1){
            if (errno == ECHILD) {
                printf("NO MORE CHILDREN\n");
                exit(0);
            } else {
                perror("wait error");
                exit(1);
            }
        }
        ++num_dead;
        printf("wait() returned 1 child (%d)\n", (int)child_pid);
    }
    return(0);
}

Обратите внимание, что каждый дочерний элемент и родительский элемент закрывают оба дескриптора файла канала перед завершением своей работы.

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

if ((child1 = fork()) == -1)
    ...error...
else if (child1 == 0)
    launch_reader(pfd, args_1);
else if ((child2 = fork()) == -1)
    ...error...
else if (child2 == 0)
    launch_writer(pfd, args_2);

...parent...

У меня также была бы функция для инкапсуляции 'error error и exit', даже если бы она была длиной всего две строки.Кроме того, убедитесь, что сообщения от printf() заканчиваются символом новой строки, если вы действительно хотите, чтобы они появлялись своевременно.

1 голос
/ 10 сентября 2011

Я отметил проблему ниже.

switch(reader_child_pid = fork()) {
    // ...
    default:
        close(pfd[1]); // <--------------------
        close(pfd[0]);
        switch(writer_child_pid = fork()) {
            // ...
            default:
                break;
        }
}

Здесь, перед разветвлением второго потомка, вы закрываете оба конца канала в родительском элементе.Поэтому оба конца закрыты и во втором дочернем процессе.Вы должны переместить эти операторы закрытия во второй блок default, чтобы они были закрыты после разветвления второго потомка.

switch(reader_child_pid = fork()) {
    // ...
    default:
        switch(writer_child_pid = fork()) {
            // ...
            default:
                close(pfd[1]);
                close(pfd[0]);
                break;
        }
}
...