C Программа с вилкой и трубой - PullRequest
0 голосов
/ 18 января 2020

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

Написать C программа, которая принимает 4 имени файла в качестве параметров. Программа должна будет создать 3 дочерних процесса. Каждый ребенок прочитает три символа из своего файла и сообщит их отцу через ТРУБУ. Отец проверит прочитанные символы. Если последовательность из 3 символов, полученных тремя детьми, одинакова, это будет напечатано в четвертом файле. Как только один из дочерних элементов заканчивает чтение своего файла, все остальные дочерние процессы также должны завершиться.

Мой код:

#include <stdlib.h>
#include <unistd.h> 
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>
#include <signal.h>
void primoFiglio(char *file,int pipe[]);
void secondoFiglio(char *file ,int pipe[]);
void terzoFiglio(char *file ,int pipe[]);

void main(int argc,char **argv){
    pid_t pid;
    pid_t processi[3];
    int pipe1[2],pipe2[2],pipe3[2],n1,n2,n3;
    int status;
    int fd;
    char buffer1[10],buffer2[10],buffer3[10];
        if(argc!=5){
        printf("Numero errato di parametri, riprovare.\n");
        exit(1);
    }
    else{
        if(pipe(pipe1)<0||pipe(pipe2)<0||pipe(pipe3)<0){
            perror("Pipe error");
            exit(1);
        }
        else{
            if((pid=fork())<0){
                perror("Fork");
                exit(1);
            }
            else{
                if(pid==0){
                    close(pipe1[0]);
                    primoFiglio(argv[1],pipe1);
                }
                else{
                    processi[0]=pid;
                    if((pid=fork())<0){
                        perror("Fork");
                        exit(1);
                    }
                    else{
                        if(pid==0){
                            close(pipe2[0]);
                            secondoFiglio(argv[2],pipe2);
                        }
                        else{
                            processi[1]=pid;
                            if((pid=fork())<0){
                                perror("Fork");
                                exit(1);
                            }
                            else{
                                if(pid==0){
                                    close(pipe3[0]);
                                    terzoFiglio(argv[3],pipe3);
                                }
                                else{
                                    processi[2]=pid;
                                    fd=open(argv[4],O_RDWR|O_APPEND|O_CREAT,0666);
                                    if(fd<0){
                                        perror("File");
                                        exit(1);
                                    }
                                    else{
                                        close(pipe1[1]);
                                        close(pipe2[1]);
                                        close(pipe3[1]);
                                        while((n1=read(pipe1[0],buffer1,3)>0)&&(n2=read(pipe2[0],buffer2,3)>0)&&(n3=read(pipe3[0],buffer3,3)>0)){
                                            if(n1==3&&n2==3&&n3==3){
                                                printf("%c %c %c\n",buffer1[0],buffer2[0],buffer3[0]);
                                                if(buffer1[0]==buffer2[0]&&buffer2[0]==buffer3[0]){
                                                    if(buffer1[1]==buffer2[1]&&buffer2[1]==buffer3[1]){
                                                        if(buffer1[2]==buffer2[2]&&buffer2[2]==buffer3[2]){
                                                            write(fd,buffer1,n1);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                        close(pipe1[0]);
                                        close(pipe2[0]);
                                        close(pipe3[0]);
                                        close(fd);
                                        if(wait(&status)!=-1){
                                            if(status>>8==2){
                                                kill(processi[1],SIGKILL);
                                                kill(processi[2],SIGKILL);

                                            }
                                            else if(status>>8==3){
                                                kill(processi[0],SIGKILL);
                                                kill(processi[2],SIGKILL);
                                            }

                                            else if(status>>8==4){
                                                kill(processi[0],SIGKILL);
                                                kill(processi[1],SIGKILL);
                                            }

                                        exit(0);
                                        }
                                    }

                                }
                            }
                        }
                    }

                }
            }
        }
    }
}

void primoFiglio(char *file ,int pipe[]){
    int fd,nread;
    char buffer[10];
    fd=open(file,O_RDONLY);
    if(fd<0){
        perror("File");
        exit(1);
    }
    else{
        while((nread=read(fd,buffer,3))>0){
            if(nread==3){
                write(pipe[1],buffer,nread);
            }
        }
        close(fd);
        printf("Primo figlio term\n");
        exit(2);
    }
}
void secondoFiglio(char *file ,int pipe[]){
    int fd,nread;
    char buffer[10];
    fd=open(file,O_RDONLY);
    if(fd<0){
        perror("File");
        exit(1);
    }
    else{
        while((nread=read(fd,buffer,3))>0){
            if(nread==3){
                 write(pipe[1],buffer,nread);
            }
        }
        close(fd);
        printf("Secondo figlio term\n");
        exit(3);
    }

}
void terzoFiglio(char *file ,int pipe[]){
    int fd,nread;
    char buffer[10];
    fd=open(file,O_RDONLY);
    if(fd<0){
        perror("File");
        exit(1);
    }
    else{
        while((nread=read(fd,buffer,3))>0){
            if(nread==3){
                 write(pipe[1],buffer,nread);
            }
        }
        close(fd);
        printf("Terzo figlio term\n");
        exit(4);
    }
}

Отец не может прочитать 3 символа из каждого pipe (только один), поэтому он ничего не записывает в четвертый файл. Какие-нибудь советы?

РЕДАКТИРОВАТЬ Спасибо, ваши ответы мне очень помогли. Я пытался улучшить свой код, и теперь он работает. Что вы думаете?

#include <stdlib.h>
#include <unistd.h> 
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>

void child(char *file,int pipe[]);


int main(int argc,char **argv){
    pid_t pid;
    pid_t process[3];
    int pipe1[2],pipe2[2],pipe3[2],n1,n2,n3;
    int status;
    int fd;
    char buffer1[10],buffer2[10],buffer3[10];
    if(argc!=5){
        printf("Wrong number of parameters\n");
        exit(1);
    }
    else if(pipe(pipe1)<0||pipe(pipe2)<0||pipe(pipe3)<0){
            perror("Pipe error");
            exit(1);
    }
    else if((pid=fork())<0){
                perror("Fork");
                exit(1);
    }
    else if(pid==0){
        child(argv[1],pipe1);
    }
    else{
        process[0]=pid;
        if((pid=fork())<0){
            perror("Fork");
            exit(1);
        }
        else if(pid==0){
            close(pipe1[0]); 
            close(pipe1[1]);
            child(argv[2],pipe2);
        }
        else{
            process[1]=pid;
            if((pid=fork())<0){
                perror("Fork");
                exit(1);
            }
            else if(pid==0){
                close(pipe2[0]); 
                close(pipe2[1]);
                child(argv[3],pipe3);
            }
            else{
                process[2]=pid;
                fd=open(argv[4],O_RDWR|O_APPEND|O_CREAT,0666);
                if(fd<0){
                    perror("File");
                    exit(1);
                }
                    close(pipe1[1]);
                    close(pipe2[1]);
                    close(pipe3[1]);
                    while((n1=read(pipe1[0],buffer1,3))>2&&(n2=read(pipe2[0],buffer2,3))>2&&(n3=read(pipe3[0],buffer3,3))>2){
                        printf("Reading...\n");
                        if(memcmp(buffer1,buffer2,3)==0&&memcmp(buffer2,buffer3,3)==0)
                            write(fd,buffer1,n1);
                        pid=waitpid(0,&status,WNOHANG); 
                        if(pid==process[0]){ 
                            kill(process[1],SIGKILL);
                            kill(process[2],SIGKILL);
                        }
                        else if(pid==process[1]){
                            kill(process[0],SIGKILL);
                            kill(process[2],SIGKILL);
                        }
                        else if(pid==process[2]){
                            kill(process[0],SIGKILL);
                            kill(process[1],SIGKILL);
                        }


                    }

                close(pipe1[0]);
                close(pipe2[0]);
                close(pipe3[0]);
                close(fd);
                exit(0);


            }
        }
    }    
}

void child(char *file ,int pipe[]){
    int fd,nread;
    char buffer[10];
    fd=open(file,O_RDONLY);
    if(fd<0){
        perror("File");
        exit(1);
    }
    while((nread=read(fd,buffer,3))>0){
        if(nread==3){
            write(pipe[1],buffer,nread);
        }
    }
    close(fd);
    exit(0);


}

1 Ответ

1 голос
/ 19 января 2020

В дополнение к различным комментариям отмечу, что три функции «figlio» очень похожи друг на друга. Они также явно не закрывают достаточно дескрипторы файла канала. Каждый дочерний элемент имеет доступ ко всем 6 файловым дескрипторам из трех каналов и теоретически должен закрыть все, кроме одного файлового дескриптора, который они будут использовать для записи. Я создал функцию figlioGenerale(), которая передает 3 пары файловых дескрипторов и закрывает все, кроме конца записи одного из них. Он также получает строку тега, чтобы определить, какой дочерний элемент он представляет, и статус выхода, который он должен использовать. Блок else, который выполняет работу для родителя, также может быть разумно преобразован в функцию.

Обратите внимание, что в строках fork() используется многократное назначение:

else if ((pid = process[0] = fork()) < 0)
else if ((pid = process[1] = fork()) < 0)
else if ((pid = process[2] = fork()) < 0)

Это сделало ненужным добавление уровня отступа для выполнения присваивания массиву process (processi в оригинале). Действительно, присвоения pid можно было бы избежать, если бы в последующих тестах использовалось process[N] вместо pid (для соответствующего значения N - 0, 1 или 2).

Мне не нравится использование SIGKILL; было бы лучше использовать SIGTERM или SIGHUP для запроса завершения процессов. На самом деле нет необходимости проверять, какой процесс завершен; достаточно отправить сигнал завершения каждому из трех детей. Я использую стандартную ошибку для сообщений об ошибках. Я также организовал, чтобы дети игнорировали SIGPIPE. Это вынуждает write() вернуться с ошибкой, которая обнаруживается и нарушает запись l oop.

Существуют другие изменения, которые могут быть внесены - некоторые из них описаны в комментариях.

Все, что приводит к такому коду:

#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h> 

static void figlioGenerale(char *file, int estatus, const char *figlio,
                           int p_use[], int p_spare1[], int p_spare2[]);

int main(int argc, char **argv)
{
    pid_t pid;
    pid_t process[3];
    int pipe1[2], pipe2[2], pipe3[2], n1, n2, n3;
    int status;
    int fd;

    char buffer1[10], buffer2[10], buffer3[10];
    if (argc != 5)
    {
        printf("Numero errato di parametri, riprovare.\n");
        fprintf(stderr, "Usage: %s infile1 infile2 infile3 outfile\n", argv[0]);
        exit(1);
    }
    else if (pipe(pipe1) < 0 || pipe(pipe2) < 0 || pipe(pipe3) < 0)
    {
        perror("Pipe error");
        exit(1);
    }
    else if ((pid = process[0] = fork()) < 0)
    {
        perror("Fork");
        exit(1);
    }
    else if (pid == 0)
    {
        figlioGenerale(argv[1], 2, "Primo", pipe1, pipe2, pipe3);
    }
    else if ((pid = process[1] = fork()) < 0)
    {
        perror("Fork");
        exit(1);
    }
    else if (pid == 0)
    {
        figlioGenerale(argv[2], 3, "Secondo", pipe2, pipe1, pipe3);
    }
    else if ((pid = process[2] = fork()) < 0)
    {
        perror("Fork");
        exit(1);
    }
    else if (pid == 0)
    {
        figlioGenerale(argv[3], 4, "Terzo", pipe3, pipe1, pipe2);
    }
    else
    {
        fd = open(argv[4], O_RDWR | O_TRUNC | O_CREAT, 0666);
        if (fd < 0)
        {
            perror("File");
            exit(1);
        }
        else
        {
            close(pipe1[1]);
            close(pipe2[1]);
            close(pipe3[1]);
            while ((n1 = read(pipe1[0], buffer1, 3) == 3) &&
                   (n2 = read(pipe2[0], buffer2, 3) == 3) &&
                   (n3 = read(pipe3[0], buffer3, 3) == 3))
            {
                if (memcmp(buffer1, buffer2, 3) == 0 &&
                    memcmp(buffer1, buffer3, 3) == 0)
                {
                    write(fd, buffer1, 3);
                }
            }
            close(pipe1[0]);    /* Children will get SIGPIPE when writing */
            close(pipe2[0]);
            close(pipe3[0]);
            close(fd);
            kill(process[0], SIGTERM);
            kill(process[1], SIGTERM);
            kill(process[2], SIGTERM);
            while ((pid = wait(&status)) != -1)
                printf("PID %d exited with status 0x%.4X\n", pid, status);
        }
    }
    return 0;
}

static void figlioGenerale(char *file, int estatus, const char *figlio,
                           int p_use[], int p_spare1[], int p_spare2[])
{
    int fd, nread;
    char buffer[3];
    fd = open(file, O_RDONLY);
    signal(SIGPIPE, SIG_IGN);       /* Causes write() to return with error on EOF */

    /* Close unused ends of pipes */
    close(p_use[0]);
    close(p_spare1[0]);
    close(p_spare1[1]);
    close(p_spare2[0]);
    close(p_spare2[1]);

    if (fd < 0)
    {
        fprintf(stderr, "file %s: %s\n", file, strerror(errno));
        exit(1);
    }
    else
    {
        while ((nread = read(fd, buffer, 3)) > 0)
        {
            if (nread == 3)
            {
                if (write(p_use[1], buffer, nread) != nread)
                    break;
            }
        }
        close(fd);
        close(p_use[1]);
        printf("%s figlio term\n", figlio);
        exit(estatus);
    }
}

При запуске (программа pipe89 скомпилирована из pipe89.c), я получаю вывод, такой как:

$ (
> set -x
> rmk -u pipe89
> pipe89 pipe89.c pipe89.c pipe89.c pipe.output
> diff pipe89.c pipe.output
> ls -ld pipe89*
> pipe89 pipe89.c pipe89.c /dev/null pipe.output
> ls -dl pipe89*
> pipe89 pipe89.c pipe89.c /dev/null pipe.output
> pipe89 pipe89.c pipe89.c /dev/null pipe.output
> pipe89 pipe89.c pipe89.c /dev/null pipe.output
> )
+++ rmk -u pipe89
    gcc -O3   -g      -I./inc    -std=c11   -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes         pipe89.c -o pipe89 -L./lib  -lsoq 
+++ pipe89 pipe89.c pipe89.c pipe89.c pipe.output
Primo figlio term
Terzo figlio term
Secondo figlio term
PID 24675 exited with status 0x0400
PID 24674 exited with status 0x0300
PID 24673 exited with status 0x0200
+++ diff pipe89.c pipe.output
+++ ls -ld pipe89 pipe89.c pipe89.dSYM pipe89.sh
-rwxr-xr-x  1 jleffler  staff  13704 Jan 18 14:25 pipe89
-rw-r--r--  1 jleffler  staff   3444 Jan 18 14:11 pipe89.c
drwxr-xr-x  3 jleffler  staff     96 Jan 18 13:45 pipe89.dSYM
-rw-r--r--  1 jleffler  staff    315 Jan 18 14:24 pipe89.sh
+++ pipe89 pipe89.c pipe89.c /dev/null pipe.output
Terzo figlio term
Secondo figlio term
PID 24681 exited with status 0x000F
PID 24680 exited with status 0x000F
PID 24679 exited with status 0x000F
+++ ls -dl pipe89 pipe89.c pipe89.dSYM pipe89.sh
-rwxr-xr-x  1 jleffler  staff  13704 Jan 18 14:25 pipe89
-rw-r--r--  1 jleffler  staff   3444 Jan 18 14:11 pipe89.c
drwxr-xr-x  3 jleffler  staff     96 Jan 18 13:45 pipe89.dSYM
-rw-r--r--  1 jleffler  staff    315 Jan 18 14:24 pipe89.sh
+++ pipe89 pipe89.c pipe89.c /dev/null pipe.output
Terzo figlio term
PID 24685 exited with status 0x000F
PID 24686 exited with status 0x000F
PID 24684 exited with status 0x000F
+++ pipe89 pipe89.c pipe89.c /dev/null pipe.output
Terzo figlio term
PID 24689 exited with status 0x000F
PID 24690 exited with status 0x000F
PID 24688 exited with status 0x000F
+++ pipe89 pipe89.c pipe89.c /dev/null pipe.output
Terzo figlio term
Secondo figlio term
PID 24694 exited with status 0x000F
PID 24693 exited with status 0x000F
PID 24692 exited with status 0x000F
$ rmk pipe89 && pipe89 pipe89.c pipe89.c /dev/null pipe.output
'pipe89' is up to date.
Terzo figlio term
PID 24699 exited with status 0x0400
PID 24698 exited with status 0x000F
PID 24697 exited with status 0x000F
$ rmk pipe89 && pipe89 pipe89.c /dev/null pipe89.c pipe.output
'pipe89' is up to date.
Secondo figlio term
PID 24708 exited with status 0x0300
PID 24709 exited with status 0x000F
PID 24707 exited with status 0x000F
$ rmk pipe89 && pipe89 /dev/null pipe89.c pipe89.c pipe.output
'pipe89' is up to date.
Primo figlio term
PID 24713 exited with status 0x0200
PID 24714 exited with status 0x000F
PID 24715 exited with status 0x000F
$

По какой-то причине, когда программа запускается из вспомогательной оболочки, все процессы завершаются сигналом SIGTERM (0x000F), но при запуске непосредственно из командной строки один из процессов (один читает /dev/null) ) выходит со своим собственным статусом выхода. Я должен описать это как причину планирования процессов.

Возможно, было бы лучше, если бы каналы создавались в одном массиве файловых дескрипторов, а дочерним процессам было сказано, из какого дескриптора канала читать. Дочерний объект может скопировать это в файловый дескриптор 1 (стандартный вывод - dup2(pipes[out], STDOUT_FILENO)), а затем просто закрыть все 6 переданных ему дескрипторов.

int pipes[6];
if (pipe(&pipes[0]) < 0 ||
    pipe(&pipes[2]) < 0 ||
    pipe(&pipes[4]) < 0)

…

figlioGenerale(argv[1], 2, "Primo",   1, pipes);
figlioGenerale(argv[2], 3, "Secondo", 3, pipes);
figlioGenerale(argv[3], 4, "Terzo",   5, pipes);
...