简体   繁体   English

带有叉子和管道的 C 程序

[英]C Program with fork and pipe

I'm at the beginning with these things and I'm trying to do this exercise but I'm clearly doing something wrong but I don't know what.我刚开始做这些事情,我正在尝试做这个练习,但我显然做错了什么,但我不知道是什么。

Write a C program that takes 4 file names as parameters.编写一个以 4 个文件名作为参数的 C 程序。 The program will have to create 3 child processes.该程序必须创建 3 个子进程。 Each child will read three characters from his file and communicate them to his father via PIPE.每个孩子将从他的文件中读取三个字符,并通过 PIPE 将它们传达给他的父亲。 The father will check the characters read, if the sequence of 3 characters received by the 3 children is the same, this will be printed in the fourth file.父亲会检查读取的字符,如果3个孩子收到的3个字符的顺序相同,则将其打印在第四个文件中。 As soon as one of the children finishes reading its file, all other child processes must also end.一旦其中一个子进程读完其文件,所有其他子进程也必须结束。

My code:我的代码:

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

Father can't read 3 characters from each pipe (only one) so it doesn't write anything into fourth file.父亲无法从每个管道中读取 3 个字符(只有一个),因此它不会将任何内容写入第四个文件。 Any tips?有小费吗?

EDIT Thank you, your answers helped me a lot I tried to improve my code and now it works What do you think?编辑谢谢,你的回答对我帮助很大 我试图改进我的代码,现在它可以工作 你觉得如何?

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


}

In addition to the various comments, I note that the three 'figlio' functions are very similar to each other.除了各种评论之外,我还注意到三个“figlio”函数彼此非常相似。 They also do not explicitly close enough pipe file descriptors.它们也没有明确关闭足够多的管道文件描述符。 Each child has access to all 6 file descriptors from the three pipes, and should, in theory, close all but the one file descriptor they will use to write on.每个孩子都可以访问来自三个管道的所有 6 个文件描述符,理论上应该关闭所有文件描述符,除了他们将用于写入的一个文件描述符。 I created a figlioGenerale() function which is passed 3 pairs of file descriptors and closes all except the write end of one of those.我创建了一个figlioGenerale()函数,它传递了 3 对文件描述符,并关闭了除其中一个文件描述符之外的所有文件描述符。 It also gets a tag string to identify which child it represents, and the exit status it should use.它还获取一个标签字符串来标识它代表哪个孩子,以及它应该使用的退出状态。 The else block which does the work for the parent could sensibly be made into a function too.为父级工作的else块也可以明智地变成一个函数。

Note that I use a multiple assignment in the fork() lines:请注意,我在fork()行中使用了多重赋值:

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

That made it unnecessary to add a level of indentation to do the assignment to the process array ( processi in the original).这使得没有必要添加缩进级别来对process数组(原始中的processi )进行分配。 Indeed, the assignment to pid could be avoided if the subsequent tests used process[N] instead of pid (for an appropriate value of N0 , 1 , or 2 ).实际上,分配给pid可以避免如果随后的测试中使用的process[N]而不是pid (为的适当值N - 01 ,或2 )。

I don't like the use of SIGKILL;我不喜欢使用 SIGKILL; it would be better to use SIGTERM or SIGHUP to request processes exit.最好使用 SIGTERM 或 SIGHUP 来请求进程退出。 There really isn't a need to check which process terminated;确实没有必要检查哪个进程终止了; it is sufficient to send the terminate signal to each of the three children.向三个孩子中的每一个发送终止信号就足够了。 I use standard error for error messages.我对错误消息使用标准错误。 I also arranged for the children to ignore SIGPIPE.我还安排孩子们无视SIGPIPE。 That forces the write() to return with an error, which is spotted and breaks the writing loop.这迫使write()返回一个错误,该错误被发现并中断写入循环。

There are other changes that could be made — some of them are outlined in the comments.还可以进行其他更改 - 其中一些在评论中进行了概述。

All that leads to code like this:所有这些都会导致这样的代码:

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

When run (program pipe89 compiled from pipe89.c ), I get outputs such as:当运行(程序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
$

For some reason, when the program is run from a sub-shell, the processes are all terminated by the SIGTERM signal ( 0x000F ), but when run directly from the command line, one of the processes (the one reading /dev/null ) exits with its own exit status.出于某种原因,当程序从子 shell 运行时,所有进程都被 SIGTERM 信号( 0x000F )终止,但是当直接从命令行运行时,其中一个进程(读取/dev/null的进程)以自己的退出状态退出。 I have to put that down to the quirks of process scheduling.我必须把它归结为进程调度的怪癖。

It might be better if the pipes were created in a single array of file descriptors, and the child processes were told which pipe descriptor to read from.如果管道是在单个文件描述符数组中创建的,并且告诉子进程从哪个管道描述符读取,则可能会更好。 The child might duplicate that to file descriptor 1 (standard output — dup2(pipes[out], STDOUT_FILENO) ) and then simply close all 6 descriptors passed to it.孩子可能会将其复制到文件描述符 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM