1

通过使用 UNIX 管道进行进程同步,我们是否会陷入饥饿?例如:

void pipesem_wait(struct pipesem *sem)
{
    char onebyte = 'A';
    if ( read( sem->rfd, &onebyte, sizeof( onebyte ) ) != sizeof( onebyte ) ) {
        perror( "read from pipe" );
        exit( 1 );
    }
}

这就是我们从管道中读取的方式。当多个进程想要从该管道中读取时,是否确定所有请求都将以特定(例如 FIFO)顺序处理,或者甚至可能永远不会发生仍有可能饥饿?

4

1 回答 1

1

您无法保证许多读取进程中的哪一个会读取数据,但您可以保证其中有一个会读取每个字节。管道实际上只是内核内部的一个共享缓冲区,具有不同的文件句柄来读取和写入它 - 如果多个进程共享这些句柄,则由调度程序决定哪个进程获取数据。

因此,如果您的意思是饥饿,因为一个或多个进程不会读取任何数据,那很有可能 - 如果数据写入速度足够慢,以至于一个进程可以像写入一样快地消耗它,那么其他进程可能看不到任何数据数据。另一方面,它可能会在所有进程中循环——它只取决于调度是如何发生的。您不能依赖任何一种情况,并且行为可能取决于 Unix 风格,甚至该风格的版本和运行它的硬件。

但是,您可以依赖所有被消耗的数据而不会丢失任何数据,并且您可以依赖以 FIFO 顺序读取的数据。然而,想象一下,一个写入"ABC"完成,进程 1 读取它,然后一个写入"DEF"完成,进程 2 读取它。无法保证这些进程将被调度,使得进程 1 在进程 2 之前完成其输入的处理。因此,虽然从管道读取数据的顺序是 FIFO,但之后又取决于进程的调度方式。

正如下面的第一个评论者指出的那样,还值得一提write()的是,只要您写入PIPE_BUF的数据价值低于数据(例如,在我的 Linux 系统上是 512 字节 - 检查limits.h您的等价物),对管道的调用就是原子的。这保证了数据块不会与来自任何其他进程写入同一管道的数据交错。如果超出此限制,则标准不会指定是否write()是原子的。还要记住,对于大数据块,您可能会得到部分写入,您应该始终处理。有关更多信息,请参阅评论者链接的 SO 问题和答案

但是,您似乎一次读取一个字节的数据,所以我推测您一次只写入一个字节并将其用作某种进程同步机制。您可能想考虑将共享内存与 pthreads 条件变量一起使用,这可能是实现相同目标的一种更优雅的方式 - 我有一些旧的演示代码,我已将其放在此处

注意:如果可移植性对您很重要,那么您可能希望坚持使用管道 - 我怀疑它们更有可能在最广泛的平台上工作。pthreads 方法应该是相当可移植的,但共享内存可能要少一些。

简而言之,如果您使用它从池中唤醒工作进程并且您不关心使用哪个进程,则管道可以作为 IPC 机制正常工作。但是,如果您希望唤醒特定进程,则需要为每个工作人员使用管道或其他机制。例如,使用 pthreads 条件变量,您可以通过调用pthread_cond_broadcast()而不是pthread_cond_signal().

这是否回答你的问题?

于 2013-01-30T19:28:38.263 回答