C 不关闭冲洗管

Flushing pipe without closing in C

我发现这里有很多线程询问如何在写入管道后刷新管道而不关闭它。 在每个线程中我都可以看到不同的建议,但我找不到明确的解决方案。

这里是一个简短的总结:

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

  2. 它也可以通过使用 ptmx 而不是管道来完成,但人们说它可能太多了。

注意:无法对管道使用 fsync

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

编辑:

当发送方想写入n个字符而客户端读取m个字符(其中m>n)时,flush会很方便。客户端将阻塞等待另一个 m-n 个字符。如果发件人想再次与客户端通信,则让他无法选择关闭管道,只发送确切的字节数可能是一个很好的错误来源。

接收器是这样操作的,不能修改:

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

所以发件人希望得到处理:"file1" 和 "file2" 为此必须:

write(pipe[1], "file1[=11=]*95", 100);
write(pipe[1], "file2[=11=]*95", 100);

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

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

(使用读写)

fflush() 意义上的刷新与管道无关,因为它们不表示为 C 流。因此没有要刷新的用户空间缓冲区。同样,fsync() 也与管道无关,因为数据没有后端存储。成功写入管道的数据存在于内核中,并且只存在于内核中,直到它们被成功读取,所以 fsync() 没有工作要做。总体而言,刷新/同步仅适用于涉及中间存储的情况,管道不是这种情况。

经过澄清,您的问题似乎与建立通过管道进行通信的消息边界有关。您是正确的,关闭管道的写入端将发出边界信号——不仅仅是一条消息,而是整个通信流——当然这是最终的。你也是正确的,没有固有的消息边界。尽管如此,您似乎至少是出于某种误解而工作:

The easiest way to avoid read blocking on the pipe would be to write the exact number of bytes that is reading.

[...]

The flush would be convenient when the sender wants to write n characters but the client reads m characters (where m>n). The client will block waiting for another m-n characters.

reader 是否会阻塞完全取决于 reader 的实现方式。特别是,read(2) 系统调用决不能保证在返回之前传输请求的字节数。在某些情况下,它可以并且将会执行简短的读取。虽然细节未指定,但您通常可以依靠短读,至​​少可以无阻塞地传输一个字符,而不是请求的全部字符。类似适用于 write(2)。因此,避免 read() 阻塞的最简单方法是确保为该 read() 调用传输至少写入 一个 字节到管道。

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

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

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

  • Delimited messages. 接收方然后读取,直到发现收到消息分隔符(换行符)或一个空字节,例如)。在这种情况下,接收方将需要为消息边界与 read() 调用传输的字节序列不对齐的可能性做好准备。通过关闭通道来标记消息的结束可以被认为是这种替代方案的特例。

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

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