【问题标题】:How to read a long from a pipe?如何从管道中读取长度?
【发布时间】:2011-07-05 22:38:29
【问题描述】:

这涉及进程间通信中的未命名管道。 我有一个管道,一个进程在其中存储一个值,另一个进程想要读取这个数值,它是整数或长整数。

这里很好地描述了http://tldp.org/LDP/lpg/node11.html 如何在 C 中创建管道。我的问题是如何从管道中读取 long 或 int。

摘自上述页面:

/* Read in a string from the pipe */
int nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);

好吧,在一般情况下,我不知道如何在 C 中处理管道(它就像一个文件?)以及如何从中读取字符串以外的数据。

【问题讨论】:

  • 在 Unix 上,everything is a file.
  • 可能要补充:我想只用printf("%d",intnum)发送,所以子进程的标准输入应该传送到这个管道。我应该添加一个换行符吗?比如 printf("%d\n",intnum) ---很容易看出我并没有真正理解 c 或 c++ 中的输入和输出。任何建议或阅读建议表示赞赏....另外,像 3867 这样的 int 的大小是多少?我猜是字节......也许是 4?

标签: c++ c linux pipe


【解决方案1】:

您不能“真正”从管道中读取long。你从管道中读取了一个字节序列,如果你可以定义一些协议来代表这些字节,那么你已经读取了一个 long。

假设管道的两端对long 使用相同的存储表示,如果它们是使用相同的编译器为相同的架构编译的,或者就此而言使用不同的编译器但使用相同的 ABI,它们就是这样,然后你可以在一端write(fd, &src_long, sizeof(long));,在另一端read(fd, &dst_long, sizeof(long));。加上或减去通常的混乱,以确保 I/O 不会提前完成。

【讨论】:

  • 您可能需要补充一点,您可以使用fdopen(3) 将管道作为标准输入输出文件打开,然后可以与fscanf 一起使用以文本形式而不是二进制形式读取数据。
  • Adam:好吧,你已经添加了它。同意,ASCII 也是一个不错的表示选择。
【解决方案2】:

管道就像一个文件,除了它是不可搜索的。当您读取数据时,它会从管道中“消失”并且无法再次读取。

您将像从任何其他文件中读取数据结构一样。可以使用 scanf、fstream>> 或 read() 和 union 来完成。

【讨论】:

    【解决方案3】:

    由于管道的两端都在同一台机器上,因此您不必担心机器架构的差异;如果您将问题更改为讨论套接字,您将不得不担心这一点。

    您可以使用以下方法将 long 写入管道:

    long l = 0x01020304L
    if (write(pipe_w_fd, &l, sizeof(l)) != sizeof(l))
        ...handle error...
    

    您可以使用以下方法从管道中读取值:

    long l;
    if (read(pipe_r_fd, &l, sizeof(l)) != sizeof(l))
        ...handle error...
    

    如果您必须处理不同的机器架构,您可以将值格式化为与平台无关的格式(例如大端格式)并在一端写入,在另一端读取与平台无关的数据,然后转换回本地机器特定格式。

    【讨论】:

    • 看起来不错,除了我真的必须使用像 printf 这样的标准输入进行写入(来自子进程的被调用进程不知道管道,但可以继承标准输入,至少已描述在给定的网站上)。但我可以这样尝试
    • @coooop:使用frwrite()fread() 然后加上适当的参数,并记住在适当的时候也刷新。适合管道缓冲区的消息(有时小到 4096,但比 long 的大小要大)作为原子数据维护(除非您只尝试读取消息的一部分)。这有很大帮助。您可能还需要在写入端使用fdopen() 来创建输出文件流,如果它必须是标准输出,可能还需要使用freopen()
    • 不同意您关于“管道两端”的假设。如果编译器不同怎么办?如果管道的另一端最终对在某种仿真模式下运行的二进制文件执行exec 怎么办?如果它执行exec 导致ssh 命令或类似命令怎么办?最后,即使你认为你可以回答这些问题“它不会发生”......为什么不让这些场景成为可能呢?这不是很多额外的努力,而且最好编写可以重新使用的代码。
    • @asveikau:我提到了这个问题——其他一些答案甚至没有提到它。很难说出对这样的家庭作业问题的期望。我认为通常最好提供满足即时要求的准确信息,并指出在“商业级”编程中可能需要额外工作的地方。不过,在这个阶段,它可能会用太多的细节来压倒 O/P。
    • 请注意,由于 32 位 x86 二进制文件在 64 位 x86-64 操作系统上运行(SPARC64 上的 SPARC 类似),在同一台机器上并不能保证它曾经是。
    【解决方案4】:

    取决于它的发送方式。如果您有一些协议/框架约定,则必须阅读框架然后提取 int。如果您只发送 int 本身,则可以读取 sizeof(int) 字节,然后直接使用缓冲区中的字节:

    int foo = *((int*) readbuffer);
    

    由于管道是本地的,因此您不必(在大多数情况下)在这里关心字节顺序和大小。

    【讨论】:

    • 啊,谢谢。但另一件事:像 3867 这样的 int 的大小是多少?
    • int 的大小始终为 sizeof(int)。值无关紧要。
    • 您的示例*((int*) readbuffer) 将在需要对齐访问整数的平台上崩溃。如果您要存储ints,不妨将readbuffer 声明为ints 的数组。
    【解决方案5】:

    好吧,总的来说,我不知道如何在 C 中处理管道(它就像一个文件?)

    是的。

    以及我如何从中读取字符串以外的数据。

    这是一种天真的情况,它假设 long 是二进制的并且在系统中大小相同。如果 long 被写成一个字符串,读取一个字符串然后找到 long。

    long mylong;
    int nbytes = read(fd[0], &mylong, sizeof(long));
    

    【讨论】:

    • 是二进制的吗?定义二进制(大端、小端等)
    • 假设它的大小相同?怎么会不一样?
    【解决方案6】:

    它被视为一个文件。我会阅读 scanf 以查看您可以轻松解析哪些数据。整数、十六进制或八进制整数之类的东西可以很容易地解析,甚至指针也可以解析。 Long 并不那么简单,但您可以从字符串 (%s) 中解析 long 并将其存储在 long 之后。

    http://beej.us/guide/bgc/output/html/multipage/scanf.html

    【讨论】:

    • 指针可以被解析,但除非你使用共享内存,否则你不应该跨管道发送指针!由于虚拟内存,一个进程中指针的值对任何其他进程完全没有意义。
    【解决方案7】:

    你可以这样做:

    long l;
    
    if (read(fd[0], &l, sizeof(l)) != sizeof(l))
    {
       /* TODO: handle this */
    }
    

    通过将序列化类型设置为long,您会失去一些灵活性,因为读取此数据的人可能会以不同的字节顺序或long 的不同大小结束。想象一下,作为一个例子,你 forkdup2(fd[1], 1) t hen 最终在子进程中exec-ing SSH 命令......现在数据可能来自另一台机器,并且会有问题。为避免这种情况,您可以执行以下操作:

    /* Type of l has a predictable size. */
    uint32_t l;
    
    if (read(fd[0], &l, sizeof(l)) != sizeof(l))
    {
       /* TODO: handle this */
    }
    
    /* Convert byte order of what we just read */
    l = ntohl(l);
    

    实际上,read 以 32 位为增量有点奇怪...您应该考虑的是提出一种消息格式并一次读取更大的数据块。实现这一点的一个好方法是为每个数量的信息设置一个标头,指示后续内容的大小、消息类型等。或者,如果您不觉得这很吸引人,您可以考虑将您的序列化为文本。这也可以很好地解决字节序问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      相关资源
      最近更新 更多