【问题标题】:Communication between processes - pipe and fifo进程之间的通信 - 管道和先进先出
【发布时间】:2018-12-20 14:45:05
【问题描述】:

我需要创建具有 3 个进程的程序:

  1. 第一个进程应重复读取/dev/urandom 并通过管道在每个周期向第二个进程发送 15 个字符。
  2. 第二个进程应将接收到的数据转换为十六进制,并通过 fifo 将结果发送到第三个进程。
  3. 第三个进程应该打印接收到的数据。

这是我到目前为止写的。使用管道的通信工作正常,但是 fifo 存在一些问题 - 当我将 n 更改为更大的数字(例如 100000 或 1000000)时,程序无法启动。当它较小时,比如 500 或 1000,程序可以工作。这可能是什么原因?

这就是我运行它的方式:

cat /dev/urandom | ./a.out

这里是代码:

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

#define FIFO "my_fifo"

int main(void) {
    int pdesk[2];
    char buf[15];
    char buffer[15];
    char hex[30];
    char f[30];
    int len;
    int n;

    n = 100;

    umask(0);
    mkfifo(FIFO, 0666);

    pipe(pdesk);

    if (fork() == 0) {
        for (int i = 0; i < n; i++) {
            read(STDIN_FILENO, buffer, 15);
            write(pdesk[1], buffer, 15);
        }
        close(pdesk[1]);
    } else {
        sleep(1);
        int fp;

        for(int i = 0; i < n; i++) { 
            read(pdesk[0], buf, 15);

            for(int a = 0, b = 0; b < 30; ++a, b+= 2) 
                sprintf(hex + b, "%02x", buf[a] & 0xff);

            fp = open(FIFO, O_WRONLY);
            write(fp, hex, 30);
            close(fp);
            usleep(10000);
        }
        close(pdesk[0]);
    }

    if (fork() == 0) {
        sleep(2);
        int fp;

        for (int i = 0; i < n; i++) {
            fp = open(FIFO, O_RDONLY);
            read(fp, f, 30);
            printf("Odczytano: %s\n", f);
            close(fp);
            usleep(10000);
        }
    }    
}

【问题讨论】:

  • 难怪你不知道实际发生了什么:代码中没有一个错误检查。首先解决这个问题会向您揭示答案。

标签: c linux pipe fifo mkfifo


【解决方案1】:

如果我理解您的代码正确,它将执行以下操作:

使用第一个fork,您可以启动一个从标准输入读取并写入管道的子进程。

您的父进程从管道读取并写入 FIFO。

当您的父进程完成其循环时,它会调用第二个 fork 来创建另一个子进程,该子进程将从 FIFO 中读取并打印数据。

当循环计数太大时,您将达到 FIFO 的缓冲区限制,并且父级将阻塞,因为没有进程正在从 FIFO 读取。当进程在写入 FIFO 时被阻塞时,它永远不会创建预期从 FIFO 读取的子进程。

我认为主要问题是您应该在开始从管道读取数据并写入 FIFO 的循环之前创建第二个孩子。

一些补充说明:

使用cat /dev/urandom | ./a.out,您的程序不会直接读取/dev/urandom。它从可能表现不同的管道中读取。

您应该始终检查read 的返回值。它会告诉您它已读取的字节数可能少于您要求它读取的字节数。如果你想要正好有 15 个字符,如果你得到的字符少于 15 个,你可能需要多读几次。 这同样适用于write

【讨论】:

  • 此外,我认为没有充分的理由继续打开 FIFO 并再次关闭它。这也可能导致进程阻塞,也许是无限期的,因为通常打开 FIFO 的一端会阻塞,直到另一端也被至少一个进程打开。此外,所有的打开和关闭都存在数据丢失的风险,因为在任何进程中任何一端都没有打开时,FIFO 不会保留任何数据。
  • 谢谢!我在下面添加了更大的评论
【解决方案2】:

非常感谢。当显示数据的进程在其他子进程之上时,它终于可以工作了。

使用 cat /dev/urandom | ./a.out 您的程序不会直接读取 /dev/urandom。它从可能表现不同的管道中读取数据。

我该如何改变它?

程序也需要像从 /dev/urandom 读取文件一样读取文件,例如:

cat file.txt | ./a.out

我听取了您的建议并开始检查 read 的值,现在它没有超出文件的范围。问题是我不知道如何检查调用了哪个参数(因此我无法检查文件的长度) - 如果它是 file.txt、/dev/urandom、none 或其他任何东西。我试过了

int main(char argc, char* argv[])

但不管我怎么称呼,argv 总是 ./a.out。有什么方法可以检查吗?

【讨论】:

  • 你可以使用./a.out &lt; /dev/urandom而不是./a.out &lt; /dev/urandom而不改变你的程序。你可以用任何文件替换/dev/urandom
  • 我认为尝试检查输入文件的大小不是一个好主意,因为/dev/urandom 或作为输入的管道没有大小。您可以查看read 的返回码以了解您何时到达文件末尾。
  • 如果您想在argc 中查看输入文件的名称,您必须将程序调用为./a.out /dev/urandom./a.out file.txt 并处理命令行参数并在程序中打开文件.如果您希望同时允许将文件名指定为命令行参数 (./a.out file.txt) 或从 shell 重定向输入 (./a.out &lt; file.txt),则需要付出更多努力。
  • @Bodo 谢谢。多个进程是否正常,如果没有usleep,它们有时会去同步并最终停止工作?当我将 usleep 的参数更改为较小的值时也会发生同样的情况,例如 usleep(10)usleep(100)
  • 我没有分析在您的程序中更改usleep 持续时间可能产生的影响以及您是否仍然存在死锁条件。无法保证您的流程的时间安排。当一个进程正在等待某事时,操作系统将运行另一个进程。使用并行进程可能会产生死锁。如果在您更改某些睡眠时间时出现阻塞,您应该尝试分析进程正在等待什么。可能有一些僵局。您还应该检查所有系统调用或库函数的返回码并处理意外结果。
猜你喜欢
  • 2013-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-10
  • 2016-04-22
  • 1970-01-01
  • 1970-01-01
  • 2010-12-26
相关资源
最近更新 更多