繁体   English   中英

从 C 中的 Pipe 读取

[英]Reading from Pipe in C

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

当管道返回-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;
}

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

这是因为在过程中你关闭了两次 pipe,做

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]

管道的用法很奇怪,可能没有你想要的:

  • fdmin1进程和自身之间的 pipe,进程执行if(write(fdmin1[1], &temp1, sizeof(int)) == -1)和稍后if(read(fdmin1[0], &min1, sizeof(int)) == -1)所以 pipe 没用, min1temp1

  • 进程执行if(write(fdmax1[1], &temp2, sizeof(int)) == -1)但没有人读取该值, pipe 没用, temp2 = findMax(0, num1, filename); 是白做的。

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

  • 第三个创建的进程执行if(write(fdmin4[1], &temp7, sizeof(int)) == -1)if(write(fdmax4[1], &temp8, sizeof(int)) == -1)但没有人阅读,这两个管道没用,最小/最大计算是徒劳的。

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

编码

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

由所有子进程执行,因为当你不得不做的时候你不会exitreturn

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

除了在过程中您没有从文件中的右侧 position 读取,因为对于findMinfindMax参数start对应于int的等级而不是文件中的 position,您必须替换

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 too many doing

 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"); 失败你必须停止执行,目前你继续一个未定义的行为。

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


回到声明

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

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

这是通过 4 个过程完成的

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

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

  • 拥有这么多管道是没有用的,只有一个就足以让每个孩子发送它计算的最小值/最大值,因为 pipe 的读写保证是原子的,直到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中生成 1000000 int ,编译并执行我的建议:

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$ 

暂无
暂无

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

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