带叉子和管道的 C 程序

C Program with fork and pipe

我刚开始做这些事情,我正在尝试做这个练习,但我显然做错了什么,但我不知道是什么。

写一个C程序,以4个文件名为参数。该程序将必须创建 3 child 个进程。每个 child 将从他的文件中读取三个字符,并通过 PIPE 将它们传达给他的父亲。父亲会检查读取到的字符,如果3children收到的3个字符的顺序相同,则打印在第四个文件中。一旦其中一个 children 完成读取其文件,所有其他 child 进程也必须结束。

我的代码:

#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 个字符(只有一个),因此它不会将任何内容写入第四个文件。 有小费吗?

编辑 谢谢,你的回答对我帮助很大 我试图改进我的代码,现在它可以工作了 你怎么看?

#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);


}

除了各种评论,我还注意到三个'figlio'函数彼此非常相似。它们也没有明确关闭足够的管道文件描述符。每个 child 都可以访问三个管道中的所有 6 个文件描述符,并且理论上应该关闭除它们将用于写入的一个文件描述符之外的所有文件描述符。我创建了一个 figlioGenerale() 函数,它传递了 3 对文件描述符并关闭了其中一个的写端除外的所有文件描述符。它还获取一个标记字符串来标识它代表哪个 child,以及它应该使用的退出状态。为 parent 工作的 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)进行赋值。实际上,如果后续测试使用 process[N] 而不是 pid(对于 N 的适当值 - 01, 或 2).

我不喜欢使用 SIGKILL;最好使用 SIGTERM 或 SIGHUP 来请求进程退出。确实不需要检查哪个进程终止了;向三个 children 中的每一个发送终止信号就足够了。我对错误消息使用标准错误。我还安排 children 忽略 SIGPIPE。这迫使 write() 变为 return 并出现错误,该错误被发现并中断了写入循环。

还有其他可以进行的更改 - 其中一些已在评论中进行了概述。

导致代码如下的所有内容:

#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.c编译的程序pipe89)时,我得到如下输出:

$ (
> 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
$

出于某种原因,当程序是 运行 来自 sub-shell 时,所有进程都被 SIGTERM 信号终止(0x000F),但是当 运行直接从命令行,其中一个进程(读取 /dev/null 的进程)以其自己的退出状态退出。我不得不把它归结为进程调度的怪癖。

如果在单个文件描述符数组中创建管道可能会更好,并且 child 进程被告知要从哪个管道描述符读取。 child 可能会将其复制到文件描述符 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);