【问题标题】:Reading from Pipe in C从 C 中的管道读取
【发布时间】:2020-06-07 06:55:49
【问题描述】:

我有一个程序从随机访问文件中读取,并返回文件中的最小和最大数字。一个要求是使用 fork() 和管道传输结果通过 4 个进程完成。我将文件分成 4 个块,并让每个进程评估文件的一个块。我找到每个块的最大值和最小值并将它们写入管道。最后,我将比较管道值并找到最大值和最小值。

当管道返回 -1 时,我无法读取管道。关于我做错了什么的任何见解?谢谢!

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

int findMin(int start, int end, const char * filename);
int findMax(int start, int end, const char * filename);

//Calculates minimum and maximum of a number
int main(int argc, char * argv[]) 
{   

   const char * filename;  // name of file to read
   FILE * ft;  // file handle for the file
   int pid,    // process id of this process
       num,    // the number of integer values in the file
       i,      // loop control variable for reading values
       temp=0; // used to store each value read from the file
   long size;  // size in bytes of the input file
   /*********************************************************************/

   filename = argv[1];  // read the file named on the command line

   ft= fopen(filename, "rb");
   if (ft) 
   {
       pid = getpid();
       fseek (ft,0,SEEK_END); //go to end of file
       size = ftell(ft);      //what byte in file am I at?
       fseek (ft,0,SEEK_SET); //go to beginning of file
       num = (int)size / (int)sizeof(int); // number of integer values
       printf("file size: %li bytes\n", size);
       printf("sizeof(int) = %i bytes\n",(int) sizeof(int));
       printf("how many integers = %i\n\n", num);

       fclose(ft);
   }

       //Split file size into quarters to make 4 processes
       int increment = num/4;
       int num1 = increment;
       int num2 = num1 + increment;
       int num3 = num2 + increment;
       int num4 = num;

       int status;

       int pid1 = -1;
       int pid2 = -1;

       //Pipes
       int fdmin1[2];
       int fdmax1[2];

       int fdmin2[2];
       int fdmax2[2];

       int fdmin3[2];
       int fdmax3[2];

       int fdmin4[2];
       int fdmax4[2];

       //initializing pipes
       if(pipe(fdmin1) == -1)
       {
           perror("Piping fd1 failed");
           return 0;
       }
       if(pipe(fdmax1) == -1)
       {
           perror("Piping fd2 failed");
           return 0;
       }

       if(pipe(fdmin2) == -1)
       {
           perror("Piping fd3 failed");
           return 0;
       }
       if(pipe(fdmax2) == -1)
       {
           perror("Piping fd4 failed");
           return 0;
       }

       if(pipe(fdmin3) == -1)
       {
           perror("Piping fd3 failed");
           return 0;
       }
       if(pipe(fdmax3) == -1)
       {
           perror("Piping fd4 failed");
           return 0;
       }

       if(pipe(fdmin4) == -1)
       {
           perror("Piping fd3 failed");
           return 0;
       }
       if(pipe(fdmax4) == -1)
       {
           perror("Piping fd4 failed");
           return 0;
       }

       //temp variables for pipes
       int temp1;
       int temp2;
       int temp3;
       int temp4;
       int temp5;
       int temp6;
       int temp7;
       int temp8;


       pid1 = fork();
       printf("pid1: %d \n", pid1);

       if(pid1 > 0)
       {
           //Process 1
           temp1 = findMin(0, num1, filename);
           temp2 = findMax(0, num1, filename);

           close(fdmin1[0]);
           if(write(fdmin1[1], &temp1, sizeof(int)) == -1)
           {
               printf("Error writting to pipe");
           }
           close(fdmin1[1]);
           close(fdmax1[0]);
           if(write(fdmax1[1], &temp2, sizeof(int)) == -1)
           {
               printf("Error writting to pipe");
           }
           close(fdmax1[1]);
       }
       else if(pid1 == 0)
       {
           //Process 2
           temp3 = findMin(num1, num2, filename);
           temp4 = findMax(num1, num2, filename);

           close(fdmin2[0]);
           if(write(fdmin2[1], &temp3, sizeof(int)) == -1)
           {
               printf("Error writting to pipe");
           }
           close(fdmin2[1]);
           close(fdmax2[0]);
           if(write(fdmax2[1], &temp4, sizeof(int)) == -1)
           {
               printf("Error writting to pipe");
           }
           close(fdmax2[1]);

           pid2 = fork();
           printf("pid2: %d \n", pid2);
           if(pid2 > 0)
           {
               //Process 3
               temp5 = findMin(num2, num3, filename);
               temp6 = findMax(num2, num3, filename);

               close(fdmin3[0]);
               if(write(fdmin3[1], &temp5, sizeof(int)) == -1)
               {
                   printf("Error writting to pipe");
               }
               close(fdmin3[1]);

               close(fdmax3[0]);
               if(write(fdmax3[1], &temp6, sizeof(int)) == -1)
               {
                   printf("Error writting to pipe");
               }
               close(fdmax3[1]);

           }
           else if(pid2 == 0)
           {
               //Process 4
               temp7 = findMin(num3, num4, filename);
               temp8 = findMax(num3, num4, filename);

               close(fdmin4[0]);
               if(write(fdmin4[1], &temp7, sizeof(int)) == -1)
               {
                   printf("Error writting to pipe");
               }
               close(fdmin4[1]);

               close(fdmax4[0]);
               if(write(fdmax4[1], &temp8, sizeof(int)) == -1)
               {
                   printf("Error writting to pipe");
               }
               close(fdmax4[1]);
           }

       }

       //Close all pipe ends in all processes
       close(fdmin1[0]);
       close(fdmin1[1]);
       close(fdmin2[0]);
       close(fdmin2[1]);
       close(fdmin3[0]);
       close(fdmin3[1]);
       close(fdmin4[0]);
       close(fdmin4[1]);

       close(fdmax1[0]);
       close(fdmax1[1]);
       close(fdmax2[0]);
       close(fdmax2[1]);
       close(fdmax3[0]);
       close(fdmax3[1]);
       close(fdmax4[0]);
       close(fdmax4[1]);

       //Wait for all processes to finish
       int returnStatus;    
       waitpid(pid1, &returnStatus, 0);

       int returnStatus2;    
       waitpid(pid2, &returnStatus2, 0);

       //Make sure we are in parant process
       if(pid1 > 0)
       {
           //Variables to compare min and max returned from processses
           int min1;
           int max1;
           int min2;
           int max2;
           int min3;
           int max3;
           int min4;
           int max4;

           //read from pipe (error is occuring here)
           close(fdmin1[1]);
           if(read(fdmin1[0], &min1, sizeof(int)) == -1)
           {
               printf("Error reading");
           }
           close(fdmin1[0]);
           printf("min1: %d \n", min1);
       }



   return 0;

}

//function to find the minimum in the file
int findMin(int start, int end, const char * filename)
{
   int temp;
   int smallestNum;
   int i;
   int length = end - start;

   FILE * ft2;
   ft2= fopen(filename, "rb");
   fseek (ft2,start,SEEK_SET);

   fread(&smallestNum,sizeof(int),1,ft2);

   for(i = 0; i < length; i++)
   {
       fread(&temp,sizeof(int),1,ft2);

       //printf("%d \n", temp);

       if(temp < smallestNum)
       {
           smallestNum = temp;
       }
       /*
       printf("%5i: %7i  ",pid,temp);
       if ((i+1)%5 == 0) 
           printf("\n");
       */
   }

   fclose(ft2);
   printf("SmallestNum: %d \n", smallestNum);

   return smallestNum;
}

//function to find maximum in file
int findMax(int start, int end, const char * filename)
{
   int temp;
   int largestNum;
   int i;
   int length = end - start;

   FILE * ft3;
   ft3= fopen(filename, "rb");
   fseek (ft3,start,SEEK_SET);

   fread(&largestNum,sizeof(int),1,ft3);

   for(i = 0; i < length; i++)
   {
       fread(&temp,sizeof(int),1,ft3);

       //printf("%d \n", temp);

       if(temp > largestNum)
       {
           largestNum = temp;
       }
       /*
       printf("%5i: %7i  ",pid,temp);
       if ((i+1)%5 == 0) 
           printf("\n");
       */
   }

   fclose(ft3);
   printf("Largest Num: %d \n", largestNum);

   return largestNum;
}

这是生成随机访问文件的代码

/*
 * This file generates a binary output file containing integers. It
 * requires the output filename as a parameter and will take an 
 * argument indicating the number of values to generate as input.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define BIAS 0 // a bias value added to the numbers to "bias" the file 
               // contents to provide an offset to the min and max

 int main(int argc, char * argv[]) {
    const char * filename;   // name of the output file
    FILE * ft;               // file handle for output file
    int numtogen = 1000000;  // default is to generate 1,000,000 numbers
    int randomnum, i; // variables used in the loop generating numbers

    if (argc<2) {  // not enough arguments, need output file name
        printf("Usage: gendata <filename> [number of numbers]\n");
        return 1;
    }
    if (argc == 3)  // optional third argument for number of numbers 
        numtogen = atoi(argv[2]);

    filename=argv[1];  // use the filename entered to store numbers
    srand(time(NULL)); // seed the random number generator
    ft= fopen(filename, "wb") ;
    if (ft) {
        for (i = 0; i < numtogen; i++){
            randomnum = rand() % numtogen + BIAS;
            fwrite(&randomnum,sizeof(int),1,ft);
        }
        fclose(ft);
    }
    return 0;
}

【问题讨论】:

    标签: c file pipe fork


    【解决方案1】:

    当管道返回 -1 时,我无法读取管道。关于我做错了什么的任何见解?谢谢!

    这是因为在 main 进程中你关闭了两次管道,这样做

    printf("pid1: %d \n", pid1);
    
    if(pid1 > 0)
    {
       ...
       close(fdmin1[0]);         <<< HERE
    

    //Close all pipe ends in all processes
    close(fdmin1[0]);            <<< HERE
    

    所以当你这样做时它会关闭:

    if(read(fdmin1[0], &min1, sizeof(int)) == -1)
    

    在读入之前不要关闭fdmin1[0],反之亦然。

    请注意,您还会关闭两次 fdmin1[1]fdmax1[0]fdmax1[1]

    管道的用法很奇怪,可能不是你想要的:

    • fdmin1main进程和自身之间的管道,main进程做if(write(fdmin1[1], &amp;temp1, sizeof(int)) == -1)和后来的if(read(fdmin1[0], &amp;min1, sizeof(int)) == -1),所以管道没用而min1temp1

    • main 进程执行if(write(fdmax1[1], &amp;temp2, sizeof(int)) == -1),但没有人读取该值,该管道无用,temp2 = findMax(0, num1, filename); 无用。

    • main 子进程执行 if(write(fdmin2[1], &amp;temp3, sizeof(int)) == -1)if(write(fdmax2[1], &amp;temp4, sizeof(int)) == -1)if(write(fdmin3[1], &amp;temp5, sizeof(int)) == -1)if(write(fdmax3[1], &amp;temp6, sizeof(int)) == -1) 但没人读,这四个管道没用,所有的最小/最大计算都是一劳永逸。

    • 第三个创建的进程也一样,做if(write(fdmin4[1], &amp;temp7, sizeof(int)) == -1)if(write(fdmax4[1], &amp;temp8, sizeof(int)) == -1),但是没人读,这两个管道没用,最小/最大计算是白做的。

    这意味着最后你在 main 过程中没有得到正确的最小/最大值,而只有 main 计算的第一季度的最小值进程和所有其他计算都丢失了。

    代码

      //Wait for all processes to finish
      int returnStatus;    
      waitpid(pid1, &returnStatus, 0);
    
      int returnStatus2;    
      waitpid(pid2, &returnStatus2, 0);
    

    由所有子进程执行,因为你在必须做的时候没有exitreturn

    您还有一个未定义的行为,因为您的进程之间存在竞争条件,根据我在您的代码中 usleep 的位置,执行是不一样的。父进程必须在需要时等待其子进程的结束,而您却不是在正确的时刻。请注意您的进程编号错误,只有 main 进程和两个子进程,因此是 3 个进程而不是 4 个,//process4 不存在,并且该注释在进程 2 中。

    除了在 main 进程中,您不会从文件中的正确位置读取,因为对于 findMinfindMax 参数 start 对应的是一个int的rank而不是文件中的一个位置,必须替换

    fseek (ft2,start,SEEK_SET);
    fseek (ft3,start,SEEK_SET);
    

    通过

     fseek (ft2,start*sizeof(int),SEEK_SET);
     fseek (ft3,start*sizeof(int),SEEK_SET);
    

    你也(试图)读到一个int太多的事情

    int length = end - start;
    ...
    fread(&smallestNum,sizeof(int),1,ft2);
    
    for(i = 0; i < length; i++)
    {
       fread(&temp,sizeof(int),1,ft2);
    

    例如将循环替换为具有

    for(i = 1; i < length; i++)
    

    如果我使用选项-Wall 编译,您的程序中还有很多无用的变量:

    bruno@bruno-XPS-8300:/tmp$ gcc -Wall -g p.c -o p
    p.c: In function ‘main’:
    p.c:250:16: warning: unused variable ‘max4’ [-Wunused-variable]
                int max4;
                    ^
    p.c:249:16: warning: unused variable ‘min4’ [-Wunused-variable]
                int min4;
                    ^
    p.c:248:16: warning: unused variable ‘max3’ [-Wunused-variable]
                int max3;
                    ^
    p.c:247:16: warning: unused variable ‘min3’ [-Wunused-variable]
                int min3;
                    ^
    p.c:246:16: warning: unused variable ‘max2’ [-Wunused-variable]
                int max2;
                    ^
    p.c:245:16: warning: unused variable ‘min2’ [-Wunused-variable]
                int min2;
                    ^
    p.c:244:16: warning: unused variable ‘max1’ [-Wunused-variable]
                int max1;
                    ^
    p.c:48:12: warning: unused variable ‘status’ [-Wunused-variable]
            int status;
                ^
    p.c:20:8: warning: unused variable ‘temp’ [-Wunused-variable]
            temp=0; // used to store each value read from the file
            ^
    p.c:19:8: warning: unused variable ‘i’ [-Wunused-variable]
            i,      // loop control variable for reading values
            ^
    p.c:17:8: warning: variable ‘pid’ set but not used [-Wunused-but-set-variable]
        int pid,    // process id of this process
            ^
    bruno@bruno-XPS-8300:/tmp$ 
    

    除此之外

    • 在执行filename = argv[1];之前必须检查argc的值。

    • 如果fopen(filename, "rb"); 失败,您必须停止执行,目前您继续执行未定义的行为。

    • 还请注意,您的程序可以使用管道数组而不是分隔变量来简化,从而允许您使用循环而不是if(pipe(fdmin1) == -1) ... if(pipe(fdmax4) == -1) ... 的序列。启动子进程也是一样的,而不是复制代码使用一个函数只写一次。这样做,您可以定义允许任意数量的子进程,而不是仅专用于 4 个。


    回到声明

    我将文件分成 4 个块,并让每个进程评估文件的一个块

    这是一个极端情况,但您必须处理文件太小而不能被 4 整除的情况,您的提案中不是这种情况。

    这是通过 4 个进程完成的

    考虑到 main 进程在 4 个中,必须创建 3 个子进程。但与其让每个子进程在需要时创建另一个子进程,不如让 main 进程创建 3 个子进程更简单,并且并行性更好一些。

    一个程序必须简单,我已经说过你有很多变量是徒劳的,还有很多代码是重复的:

    • 拥有这么多管道是没有用的,只有一个就足以让每个孩子发送它计算出的最小值/最大值,因为管道读取和写入保证是原子的,直到 PIPE_BUF(大于大小为 2 int)

    • 文件读了这么多次也没用,可以同时搜索最小值和最大值。

    最后是一个提案:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    #define N 4 /* including the main process */
    
    /* to send/receive result atomicaly through the pipe */
    typedef struct {
      int min, max;
    } MinMax;
    
    void findMinMax(long offset, long n, FILE * fp, MinMax * minmax);
    
    //Calculates minimum and maximum of a number
    int main(int argc, char * argv[]) 
    {   
      const char * filename;  // name of file to read
      FILE * fp;   // file handle for the file
      long num;    // the number of integer values in the file
      long size;   // size in bytes of the input file
      long offset; // offset in file
      int pp[2];   // the unique pipe
      int pids[N-1];
      MinMax minmax;
      int i; 
    
      if (argc != 2) {
        fprintf(stderr, "Usage: %s <filename>\n", *argv);
        exit(-1);
      }
    
      filename = argv[1];
    
      fp = fopen(filename, "rb");
      if (fp == NULL) {
        perror("cannot open file");
        exit(-1);
      }
    
      /* get file size */
      if (fseek(fp, 0, SEEK_END) == -1) { //go to end of file
        perror("cannot fseek");
        fclose(fp); /* also done automaticaly when exiting program */
        exit(-1);
      }
    
      size = ftell(fp);      //what byte in file am I at?
      num = size / sizeof(int); // number of integer values
    
      printf("file size: %li bytes\n", size);
      printf("how many integers = %li\n\n", num);
    
      if (num < N) {
        fprintf(stderr, "the input file is too small, it must contains at least %i int\n", N);
        fclose(fp); /* also done automaticaly when exiting program */
        exit(-1);
      }
    
      //initializing pipe
      if(pipe(pp) == -1) {
        perror("Piping failed");
        exit(-1);
      }
    
      offset = 0;
    
      for (i = 0; i != N-1; ++i) {
        pids[i] = fork();
    
        switch (pids[i]) {
        case 0:
          /* child */
          {
            FILE * fp2 = fopen(filename, "rb");
    
            if (fp2 == NULL) {
              perror("child cannot open file");
              exit(-1);
            }
    
            findMinMax(offset, num/N, fp2, &minmax);
            printf("min max child %d : %d %d\n", i, minmax.min, minmax.max);
            if (write(pp[1], &minmax, sizeof(minmax)) != sizeof(minmax)) {
              perror("Error writting to pipe");
              exit(-1);
            }
          }
          exit(0);
        case -1:
          /* parent */
          perror("Cannot fork");
          exit(-1);
        default:
          /* parent, no error */
          offset += (num/N)*sizeof(int);
        }
      }
    
      findMinMax(offset, (size - offset)/sizeof(int), fp, &minmax);
      printf("min max main : %d %d\n", minmax.min, minmax.max);
    
      for (i = 0; i != N-1; ++i) {
        int status;
        MinMax mm;
    
        if ((waitpid(pids[i], &status, 0) != -1) &&
            (status == 0) &&
            (read(pp[0], &mm, sizeof(mm)) == sizeof(mm))) {
          if (mm.min < minmax.min)
            minmax.min = mm.min;
          if (mm.max > minmax.max)
            minmax.max = mm.max;
        }
        else
          fprintf(stderr, "cannot get result for child %d\n", i);
      }
    
      printf("global min max : %d %d\n", minmax.min, minmax.max);
      return 0;
    }
    
    // function to find the minimum and maximum in the file
    // n > 1
    void findMinMax(long offset, long n, FILE * fp, MinMax * minmax)
    {
      int v;
    
      if (fseek(fp, offset, SEEK_SET) == -1) {
        perror("cannot fseek");
        exit(-1);
      }
    
      if (fread(&minmax->min, sizeof(minmax->min), 1, fp) != 1) {
        fclose(fp); /* also done automaticaly when exiting program */
        perror("cannot read int");
        exit(-1);
      }
      minmax->max = minmax->min;
    
      while (--n) {
        if (fread(&v, sizeof(v), 1, fp) != 1) {
          fclose(fp); /* also done automaticaly when exiting program */
          perror("cannot read int");
          exit(-1);
        }
    
        if (v < minmax->min)
          minmax->min = v;
        if (v > minmax->max)
          minmax->max = v;
      }
    
      fclose(fp); /* also done automaticaly when exiting program */
    }
    

    如您所见,代码非常简单,我只需将#define N 4 修改为其他值即可更改并行工作的进程数。

    使用你的第二个程序在aze中生成1000000int,编译并执行我的提议:

    bruno@bruno-XPS-8300:/tmp$ gcc -g -Wall p.c
    bruno@bruno-XPS-8300:/tmp$ ./a.out aze
    file size: 4000000 bytes
    how many integers = 1000000
    
    min max main : 2 999995
    min max child 0 : 10 999994
    min max child 2 : 0 999998
    min max child 1 : 3 999999
    global min max : 0 999999
    bruno@bruno-XPS-8300:/tmp$ 
    

    【讨论】:

    • 感谢您的回答。真的有助于更好地理解发生了什么。我将随机访问文件中的数字放入文本编辑器中,然后搜索我从你给我的建议中得到的最大值和最小值。我有时无法找到最大值,具体取决于我用于“N”的进程数。您是否知道为什么会发生这种行为?
    • @Erck 我不确定是否理解您,如果数字不是二进制(使用恒定大小)而是文本形式,它们的大小不是恒定的,因此无法使用 @ 987654361@ 从给定的偏移量读取,因为您无法知道要使用的偏移量
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多