从C中的管道读取

问题描述 投票:2回答:1

我有一个程序从随机访问文件中读取,并将返回文件中的最小和最大数字。一个要求是,这是通过使用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个回答
2
投票

我在从管道读取-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], &temp1, sizeof(int)) == -1),随后执行if(read(fdmin1[0], &min1, sizeof(int)) == -1),以便该管道无用,并且min1temp1

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

  • main处理子执行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)是相同的,但是没有人读过,这两个管道是无用的,最小/最大计算是一无所有。

  • ]

    这意味着最后您在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进程外,您没有从文件的正确位置读取数据,因为对于findMin

findMax,参数start对应于int 而不是文件中的位置,必须替换
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$ 

超出

  • 您必须先检查argc的值才能进行filename = argv[1];

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

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


  • 回到陈述

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

这是一个极端的情况,但是您必须处理文件太小而无法除以4的情况,这在您的建议中不是这样。

这通过4个过程完成

考虑main

进程是必须创建的4个3个孩子中的一个。但是,与让每个孩子创建另一个孩子相比,通过main流程创建三个孩子要简单得多,并且并行性要好一些。

程序必须是简单,我已经说过你有很多变量,什么也没有,而且很多代码也是重复的,也:

  • 拥有如此多的管道是没有用的,只有一个管道足以允许每个孩子发送它计算出的最小值/最大值,因为保证管道的读写操作是原子的,直到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$ 
        
© www.soinside.com 2019 - 2024. All rights reserved.