簡體   English   中英

帶有叉子和管道的 C 程序

[英]C Program with fork and pipe

我剛開始做這些事情,我正在嘗試做這個練習,但我顯然做錯了什么,但我不知道是什么。

編寫一個以 4 個文件名作為參數的 C 程序。 該程序必須創建 3 個子進程。 每個孩子將從他的文件中讀取三個字符,並通過 PIPE 將它們傳達給他的父親。 父親會檢查讀取的字符,如果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 個字符(只有一個),因此它不會將任何內容寫入第四個文件。 有小費嗎?

編輯謝謝,你的回答對我幫助很大 我試圖改進我的代碼,現在它可以工作 你覺得如何?

#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”函數彼此非常相似。 它們也沒有明確關閉足夠多的管道文件描述符。 每個孩子都可以訪問來自三個管道的所有 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 - 01 ,或2 )。

我不喜歡使用 SIGKILL; 最好使用 SIGTERM 或 SIGHUP 來請求進程退出。 確實沒有必要檢查哪個進程終止了; 向三個孩子中的每一個發送終止信號就足夠了。 我對錯誤消息使用標准錯誤。 我還安排孩子們無視SIGPIPE。 這迫使write()返回一個錯誤,該錯誤被發現並中斷寫入循環。

還可以進行其他更改 - 其中一些在評論中進行了概述。

所有這些都會導致這樣的代碼:

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

出於某種原因,當程序從子 shell 運行時,所有進程都被 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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM