在原子内容的意义上,从匿名管道原子读取

Is reading from an anonymous pipe atomic, in the sense of atomic content?

本文关键字:读取 管道 意义上      更新时间:2023-10-16

我正在Linux上编写一个具有两个线程的进程。它们使用由pipe()调用创建的匿名管道进行通信。

一端将C结构复制到管道中:

struct EventStruct e;
[...]
ssize_t n = write(pipefd[1], &e, sizeof(e));

另一端从管道中读取:

struct EventStruct e;
ssize_t n = read(pipefd[0], &e, sizeof(e));
if(n != -1 && n != 0 && n < sizeof(e))
{
    // Is a partial read possible here??
}

匿名管道可以发生部分读吗?

手册页(man 7 pipe)规定在PIPE_BUF大小下的任何写都是原子的。但他们的意思是原子对于其他作家线程…我不关心多个作家的问题。我只有一个写线程和一个读线程。

作为旁注,我的结构是56字节长。远低于PIPE_BUF大小,在Linux上至少为4096字节。在最新的内核中,它看起来甚至更高。

否则:在读端,我是否必须处理部分读和存储它们,同时我收到一个完整的结构实例?

只要处理的是固定大小的单位,就没有问题。如果您在管道上写入一个N字节的单位,并且读取器从管道请求一个N字节的单位,那么就不会有问题。如果您不能一次性读取所有数据(例如,在读取其长度之前您不知道其大小),那么生活会变得更加棘手。但是,如图所示,您应该没问题。

也就是说,您仍然应该检测到短读取。如果你只读了一小段,却认为它是完整的,那将是一场灾难。但是,您不应该期望检测到短读取-代码覆盖将是一个问题。我只是测试n < (ssize_t)sizeof(e)和任何检测到的是错误或EOF。注意演员阵容;否则,有符号值将被转换为无符号值,-1将无法被正确识别。

对于规范,您需要阅读POSIX规范:

  • read()
  • write()
  • pipe()

并可能跟踪这些页面的链接。例如,对于write(),规范说:

对管道或FIFO的写请求应以与常规文件相同的方式处理,但有以下例外:

  • 没有与管道相关的文件偏移量,因此每个写请求都必须附加到管道的末尾。

  • {PIPE_BUF}字节或更少的写请求不能与在同一管道上进行写操作的其他进程的数据交叉。无论是否设置了文件状态标志的O_NONBLOCK标志,大于{PIPE_BUF}字节的写操作可能会在任意边界上与其他进程的写操作交叉。

或来自read()的规范:

操作完成后,当nbyte大于0时,read()将标记为更新文件的最后一次数据访问时间戳,并返回读取的字节数。这个数字不能大于nbyte。如果文件中的剩余字节数小于nbyte,如果read()请求被信号中断,或者文件是管道或FIFO或特殊文件并且立即可用的读取字节少于nbyte,则返回的值可能小于nbyte。例如,来自与终端相关联的文件的read()可能返回一个键入的数据行。

所以,write()会写原子单位;read()将只读取原子单位,因为这是写入的内容。就像我一开始说的,不会有问题的。