在没有关闭C的情况下冲洗管道

时间:2017-04-03 11:56:28

标签: c pipe eof flush

我在这里找到了很多线程,询问在写入管道之后如何在不关闭管道的情况下冲洗管道。 在每个帖子中我都能看到不同的建议,但我找不到明确的解决方案。

以下是快速摘要:

  1. 避免管道上的读取阻塞的最简单方法是写入正在读取的确切字节数。

  2. 也可以通过使用ptmx代替管道来完成,但人们说它可能会有很多。

  3. 注意:无法将fsync与管道一起使用

    还有其他更有效的解决方案吗?

    编辑:

    当发件人想要写n个字符但客户端读取m个字符(m> n)时,刷新会很方便。客户端将阻止等待另一个m-n字符。如果发送者希望再次与客户端进行通信,则无需关闭管道,只发送确切的字节数可能是错误的良好来源。

    接收器的运行方式如下,无法修改:

    while((n=read(0, buf, 100)>0){
         process(buf)
    

    所以发件人想要处理:" file1"和" file2"必须:

    write(pipe[1], "file1\0*95", 100);
    write(pipe[1], "file2\0*95", 100);
    

    我正在寻找一种方法来做类似的事情(没有必要使用\ n作为分隔符):

    write(pipe[1], "file1\nfile2", 11); //it would have worked if it was ptmx
    

    (使用读写)

1 个答案:

答案 0 :(得分:2)

fflush()意义上的刷新与管道无关,因为它们不表示为C流。因此,没有用户空间缓冲区来刷新。同样,fsync()也与管道无关,因为数据没有后端存储。成功写入管道的数据存在于内核中,并且只有在内核中才能成功读取,因此fsync()无法完成。总的来说,冲洗/同步仅适用于涉及中间存储的情况,而管道则不适用。

通过澄清,您的问题似乎是关于通过管道建立通信的消息边界。你是正确的,关闭管道的写端将发出边界 - 不仅是一条消息,而是整个通信流 - 但当然这是最终的。你也没有固有的消息边界。然而,你似乎至少在某种程度上误解了这一点:

  

避免管道上的读取阻塞的最简单方法是写入   正在读取的确切字节数。

     

[...]

     

当发件人想要写n时,刷新会很方便   字符但客户端读取m个字符(其中m> n)。客户端   将阻止等待另一个m-n字符。

读者是否会阻止完全取决于读者的实施方式。特别是,read(2)系统调用决不保证在返回之前传输所请求的字节数。在某些情况下,它可以并且将执行简短的阅读。尽管未指定详细信息,但是当至少一个字符可以在不阻塞的情况下传输时,通常可以依赖于短读,但不能是所请求的整数。类似适用于write(2)。因此,避免read()阻止的最简单方法是确保至少将一个字节写入管道以进行read()传输调用。

事实上,人们通常从相反的方向来解决这个问题:需要确定接收特定数量的字节,因此必须将短读取的可能性作为并发症处理(通过执行来处理)循环中的read()。您也需要考虑这一点,但您的好处是您的客户在您描述的情况下不太可能阻止;这不是你认为的问题。

然而,在任何类型的流通信中都存在固有的消息边界问题,您需要处理它。有几种方法;最常用的是

  • 固定长度的消息。 接收器可以读取,直到成功传输所需的字节数;任何阻碍都是恰当和必要的。使用这种方法,您假设的场景不会出现,但作者可能需要填充其消息。

  • 分隔的消息。 接收器然后读取,直到它发现它已收到消息分隔符(例如换行符或空字节)。在这种情况下,接收器需要准备好消息边界与read()调用传输的字节序列不一致的可能性。通过关闭频道来标记消息的结尾可以被视为此替代方案的特例。

  • 嵌入式消息长度元数据。 这可以采用多种形式,但最简单的方法之一是将消息构造为固定长度的整数消息长度字段,后跟消息数据的字节数。然后读者知道它需要读取多少字节,因此它不会被不必要地阻塞。

这些可以单独使用或组合使用,以实现应用程序层协议,以便在进程之间进行通信。当然,通信各方必须就通信的协议细节达成一致才能成功。