c - 阻塞管道,c,linux

标签 c linux pipe blocking

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

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

我认为我需要一种不同的机制来通知每个生产者数据的结束,但我不知道如何通知消费者管道结束。我读过一些关于 fcntl 的内容,但我不知道如何在我的代码中使用它。

代码如下:

主要内容:

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);
}

制作人

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;
}

消费者:

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;
}

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

最佳答案

你让这件事变得比它需要的更难。

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

但是请注意,我说过必须关闭管道写入端的所有副本。这包括主程序中的副本,以及每个生产者和消费者中的副本。事实证明,您确实关闭了所有这些,但您依赖于生产者的终止来关闭其中每个副本中的一个副本。这有点不干净,而且它肯定是特定情况的。生产者在完成它们时明确关闭它们会是更好的风格。

此外,您的代码还存在一些其他问题和异常,其中包括:

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

  • execl() 的参数和每个进程的argv 的元素都是指向字符串 的指针,因此键入 char *,由该类型的 NULL 指针终止(不计入 argc)。您在 execl() 调用中指定了一些 int * 类型的指针,并且您的子进程假设它们接收到该类型的指针。这可能恰好对你有用,但这是不正确的,因为 parent 和 child 的行为因此都是未定义的。

关于c - 阻塞管道,c,linux,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48241561/

相关文章:

c - 将指针数组指向数组数组

c - 从指针生成整数,从整数生成指针,不进行强制转换

c - 绑定(bind)失败 : Address already in use

python-3.x - Python 3 写入管道

delphi - 在Delphi中启动两个进程并用管道连接它们

c - 输入一个字母,然后减去字母表

将 offsetof 宏的结果连接 (##) 到标识符?

linux - 在 Nagios 中创建事件

linux - 查找多线程应用程序的进程号

c - 如何使用popen同时输入和输出?