阻塞管道,c,linux

时间:2018-01-13 15:59:53

标签: c linux pipe blocking

我有一个名为" 消费者和生产者"的项目。 在其中,我的主程序创建了给定数量的消费者和生产者 子进程,通过命令行参数(./main number_of_producents number_of_consumers)指定。 每个生产者生成随机数量的随机可打印字符,将这些字符写入特定于流程的文件和主流程指定的管道。每个使用者从主进程指定的管道中读取所有字符,并将它们写入自己的特定于进程的文件。主程序完成后,生成器中的字符组合数量为'文件应与消费者中的字符组合数量相同。文件。

我遇到了如何告知消费者产品结束的问题。我试图通过让生产者在他们的数据之后写一个空字符来做到这一点,但它并没有解决我的问题,因为当我创建更多的生产者而不是消费者时,消费者不会读取所有产品(每当读取空字符时完成其工作)。当消费者的数量大于生产者的数量时,另一方面,一些消费者从未读过空字符,因此永远不会完成。

我认为我需要一种不同的机制来表示每个生产者数据的结束,但我不知道如何告知消费者有关管道末端的信息。我已经阅读了有关fcntl的内容,但我不知道如何在我的代码中使用它。

以下是代码:

MAIN:

int n;
int i,w,x;
int pipeT[2];
if(pipe(potok) != 0) exit(EXIT_FAILURE);
printf("[MAIN] Creating %d producers!\n", atoi(argv[1]));
for(i = 0; i < atoi(argv[1]); i++) {
  switch(fork()) {
      case -1:
          exit(3);
          break;
      case 0:
          if((n = dup(pipeT[1])) == -1) exit(5);
          close(pipeT[0]);
          if((execl("./producer","producer", &n, NULL)) == -1) exit(4);
          break;
      default:
          break;
  }
}
printf("[MAIN] Creating %d consumers!\n", atoi(argv[2]));
for(i = 0; i < atoi(argv[2]); i++) {
  switch(fork()) {
      case -1:
          exit(3);
          break;
      case 0: //Proces potomny
          if((n = dup(pipeT[0])) == -1) exit(5);
          close(pipeT[1]);
          if((execl("./consumer","consumer", &n, NULL)) == -1) exit(4);
          break;
      default:
          break;
  }
}
close(pipeT[0]);
close(pipeT[1]);
for (i = 0; i < atoi(argv[1]) + atoi(argv[2]); i++) {
    w = wait(&x);
    if (w == -1) exit(6);
}

PRODUCER

int main(int argc, char *argv[]) {
  srand(getpid());
    int k,t,n;
  n = *argv[1];
    char sign;
    char nameOfFile[20];
    sprintf(nameOfFile, "P/p_%d", getpid());
    FILE* f = fopen(nameOfFile, "w");
  if(f == NULL) exit (1);
    int i;
  int numberOfSigns = rand()%100;
    for(i = 0; i < numberOfSigns; i++) {
        sign = rand()%94 + 32;
    if (write(n, &sign, sizeof(char)) == -1) exit(EXIT_FAILURE);
        fprintf(f, "%c", sign);
    }
  sign = 0;
  write(n, &sign, sizeof(char));
    fclose(f);
    return 0;
}

CONSUMER:

int main(int argc, char *argv[]) {
    char sign;
  char nameOfFile[20];
  int n = *argv[1];
    sprintf(nameOfFile, "K/k_%d", getpid());
    FILE* f = fopen(nameOfFile, "w");
  if(f == NULL) exit (1);
    do {
        if ((read(n, &sign, sizeof(char))) == -1) exit(EXIT_FAILURE);
        if(sign != 0) fprintf(f, "%c" , sign);
  } while(sign != 0);
    fclose(f);
    return 0;
}

如何向消费者发出数据结束的信号,以便消费者中的消费者阅读所有数据并且所有数据都能识别出结束?

2 个答案:

答案 0 :(得分:0)

你应该更加努力地思考如何解决这个问题。例如,如果其中一个生产者比其他生产者停止得更早,那么应该阻止其中一个消费者呢?如果没有,那么生产者不应该向消费者发送代码,只有主程序应该关心他们已经终止。

我解决这个问题的方法是,消费者应该由主程序终止,而不是由制作人终止。最简单的方法是关闭管道的发送端。然后读取端将获得文件结束,即read()返回的大小为0。

在您的代码中,这可能意味着

1)不要从制片人发送0

2)让主程序等待所有生产者先终止,并在他们全部终止时(在等待消费者之前)关闭管道

3)当read()返回0时,让消费者退出

(另请注意,您的代码还有其他问题。例如,您不能通过execl传递指向整数的指针,只能传递字符串。)

答案 1 :(得分:0)

你正在努力实现这一目标。

消费者识别数据结束的自然方式是作为管道上的文件结尾。他们将以read()返回0的形式看到这一点,这将在管道写入端的所有副本关闭且没有更多数据可供从中读取之后发生。由于消费者和生产者都共享相同的管道,这也将为消费者提供一些自然的数据平衡。

但请注意,我说必须关闭管道写入端的所有副本。这包括主程序中的副本,以及每个生产者和消费者的副本。事实证明,你关闭所有这些,但你依靠生产者的终止来关闭每个人的一个副本。这有点不干净,而且肯定是针对具体情况的。对于制作人来说,当他们完成这些作品时明确地关闭它们将是更好的风格。

此外,您的代码还会出现一些其他问题和奇怪之处:<​​/ p>

  • 您对dup()的使用毫无意义。由于您无论如何都要将文件描述符号传递给子节点,您也可以让它们使用原始的管道末端文件描述符。在您这样的情况下,dup()(或更好:dup2())的通常用法是使一个或多个特定文件描述符成为您正在复制的文件描述符的副本。例如,那些标准流。请注意,如果您使用标准流,那么它们具有众所周知的文件描述符编号,因此您无需告诉孩子使用哪些FD。

  • execl()的参数和每个进程的argv的元素都是字符串的指针,因此键入char *,以{{结尾} 1}}该类型的指针(不计入NULL)。您在argc调用中指定了int *类型的一些指针,并且您的子进程假设它们接收到该类型的指针。这可能是因为这适合你,但它是不正确的,因为父母和孩子的行为都是未定义的结果。