C++ Linux命名管道在使用O_WRONLY打开时挂起

5

这是我的简单代码,它打开一个命名管道,向其中写入一个字符串,然后关闭该管道。管道是在另一个函数中创建的,如下所述。

char * ipcnm = "./jobqueue";

std::cout << "opening job queue" << std::endl;

//ensure the jobqueue is opened
if ((jobq = open(ipcnm, O_WRONLY)) < 0) {
  perror("open");
  exit(-1);
}

std::cout << "queue opened" << std::endl;

// record the number of bytes written to the queue
size_t written = write(jobq, ptr, size*nmemb);

// close fifo
if (close(jobq) < 0) {
  perror("close");
  exit(-1);
}

// need to report to other agents the size of the job that was written
jobSizes.push_back(written);

但是调用open()时卡住了。我确保在调用时没有其他进程使用fifo "jobqueue",并且一旦创建了队列,队列的文件权限设置为prwxrwxr-x(我只是使用mkfifo(ipcnm,0777)来创建管道)。
起初我以为问题在于组o在此管道上缺少w权限,因此我手动使用chmod更改了它们,但它仍然卡住了,因为“queue opened”从未被打印出来。 perror(“open”)的错误消息也不会显示。
我错过了什么?
2个回答

21

打开一个FIFO以进行写操作时,只有在有读者存在时,作者才会被阻塞。

你可能错过了读者。

你不能先写入管道,然后关闭它,再让读者稍后到来。这种存储语义是通过使用常规文件实现的。

管道是一种进程间通信机制;通过打开FIFO创建的管道类似于pipe POSIX C库函数返回的对象,只是pipe返回的对象已经准备好进行I/O,因为有两个描述符:对I/O方向开放的相反端口。而FIFO的端点是分别一次打开的。

文件系统中的FIFO对象只是一个联系点,允许多个进程连接到同一个管道上。

最初,不存在任何管道对象。当第一个进程在文件系统中的FIFO对象上执行open时,将创建一个管道。来自同一进程或另一进程的任何其他open请求都会连接到内核中保存的同一管道对象上。在至少为读取和至少为写入打开管道之前,无法进行I/O。实际的管道I/O通过内核进行;它不存储在文件系统中。当所有进程关闭管道时,该对象消失。

可以设计一个FIFO,使得在任何进程打开读取对象之前就可以开始I/O。也就是说,可以允许写请求继续进行,然后只在管道填满时阻塞。这种设计将会出现问题,例如,如果写入的数据很小,以至于管道没有填满会怎样呢?写入者将会写入数据并在执行过程中继续执行,如果它仅仅在读取器读取数据之前就退出了,那么这些数据就永远消失了!阻塞行为确保有读取器捕获数据;当写入者解除阻塞时,它可以确定有一个读取器打开了管道,因此它可以安全地关闭管道的端口而不会丢失数据。如果即使没有读取器可用,也不会阻断写入操作的设计方式,就需要在内核中一直保留管道对象,即使没有进程打开它,这样写入者可以打开管道,放入数据,然后离开,稍后读取器可以取出数据。否则,设计就必须向写入者提供阻塞式的close (类似于套接字上的SO_LINGER设置)来等待先前写入的数据被移除。


@sonukumar: 这不是“必须的”,这就是管道的作用。如果您不想使用管道,请使用文件。 - Lightness Races in Orbit
@LightnessRacesinOrbit 谢谢,但我想知道一些背后的原因。 - sonus21
@sonukumar:你为什么认为这里有一个具体的推理呢?这是管道本质的自然结果。这就像问为什么只有四个人能坐在四座位车上一样。没有人决定每个座位只能坐一个人,这是座位的自然结果。实际上,更好的比喻是当你打电话给某人时,你会被阻塞听到它响起,直到另一端的人接电话;只有他们接电话后,你才能开始与他们交谈。同样,没有人决定这应该是这种方式——这是自然的。 - Lightness Races in Orbit
1
@sonukumar 我在答案中添加了一段文本,试图解决“为什么”的问题。 - Kaz
@LightnessRacesinOrbit 我已经修正了这个答案中的错误信息。 - Kaz
显示剩余4条评论

4

使用 O_RDWR 代替 O_WRONLY 进行打开。这将会以非阻塞的方式打开 fifo,即使读者尚未打开另一端。


有点晚了,但是要提到使用标志O_RDWR打开FIFO是未定义的行为,因为命名管道不是双向的。 - wafL
@wafL 由谁定义的?Linux?POSIX?看起来像是有多个读者一样明确定义。 - Aleksandr Dubinsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接