8

我有两个进程A和B。通信流程始终是A -> B,但是我需要使用命名管道来完成,因为我必须在B进程内部的select调用中使用管道文件描述符,并且写入的数据当任何一个或两个进程退出时,管道必须持续存在。

管道在两端以非阻塞模式打开。在过程 A 中:

int push_fifo_fd = open(FIFO_NAME, O_WRONLY | O_NONBLOCK | O_CREAT, 0644);

在过程 B 中:

int fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK | O_CREAT, 0644);

Q1。进程 B 使用 curl 多接口,因此我获取 curl 多句柄的 fd_sets 并将“fd”描述符添加到读取的 fd_set,而不是调用 select,以获取可用于读取和写入的文件描述符。在每次调用 select 时,“fd”都包含在读取结果 fd_set 中,但 read 返回 0,即使写入端已打开。这会导致进程 B 使用 100% 的处理器时间。我提到我不知道打开管道末端的顺序。B的相关代码:

while (1)
{
    fd_set read_fds, write_fds, err_fds;

    FD_ZERO(&read_fds);
    FD_ZERO(&write_fds);
    FD_ZERO(&err_fds);

    FD_SET(fifo_fd, &read_fds);
    // some code
    ccode = curl_multi_fdset(curlm, &read_fds, &write_fds, &err_fds, &max_fd);
    max_fd = MAX(max_fd, fifo_fd);

    rc = select(max_fd + 1, &read_fds, &write_fds, &err_fds, &timeout);
    switch (rc)
    {
        case -1:
            WARN("select");
            continue;

        case 0:
        default:
            {
                if (FD_ISSET(fifo_fd, &read_fds))
                {
                    // read from the fifo_fd
                }

                /* Now look at the handles that need attention */
                int old_running_handles = running_handles;

                ccode = curl_multi_perform(curlm, &running_handles);
                if (ccode != CURLM_OK && ccode != CURLM_CALL_MULTI_PERFORM)
                {
                    WARN("curl_multi_perform error: %s", curl_multi_strerror(ccode));
                    continue;
                }

                if (running_handles != old_running_handles)
                {
                    CURLMsg *curl_msg;
                    int left_msgs = 0;
                    while ((curl_msg = curl_multi_info_read(curlm, &left_msgs)) != NULL)
                    {
                        // treat each easy handle
                    }
                }
            }
            break;
    }
}

Q2。在“man 7 fifo”中说“一个进程可以在非阻塞模式下打开一个FIFO。在这种情况下,即使没有人在写入端打开,打开只读也会成功,打开只写会失败使用 ENXIO(没有这样的设备或地址),除非另一端已经打开。” 但是进程A总是可以在非阻塞模式下成功打开管道的写端,即使读端没有打开。这是为什么?我测试的平台是 Ubuntu 服务器 12.04.3,内核 3.8.0-29。

4

2 回答 2

2

Q1 由select()或预期poll()。请参阅链接的问题。一个优雅的解决方案是在同一个 fifo 上打开另一个 fd 并关闭原始文件。

我相信在某些版本的内核上也会出现 Q2。man 7 fifo有一段关于它的内容:

   Under Linux, opening a FIFO for read and write will succeed both in 
   blocking and nonblocking mode.  POSIX leaves this behavior undefined.
   This can be used to open a FIFO for writing while there are no
   readers available.

正如原作者在 Q2 中所观察到的那样,该段似乎声称您可以随时成功打开 fifo 的写入端。

尽管它似乎与上一段相矛盾,因为原始问题也来自man 7 fifo页面,该页面基本上是说 open 将失败而不是成功:

   A process can open a FIFO in nonblocking mode.  In this case, opening
   for read-only succeeds even if no one has opened on the write side
   yet and opening for write-only fails with ENXIO (no such device or
   address) unless the other end has already been opened.

我看到在 4.9.37 内核上,当读取端未打开时,以非阻塞模式打开写入端将失败。我猜它一定是从 3.8 版更改为 4.9 版。

于 2018-08-25T17:00:23.357 回答
0

因为:

在 Linux 下,打开 FIFO 进行读写将在阻塞和非阻塞模式下都成功。POSIX 未定义此行为。这可用于在没有可用读取器时打开 FIFO 进行写入。一个使用连接两端来与自己通信的进程应该非常小心以避免死锁。

来自http://linux.die.net/man/7/fifo

于 2015-12-09T20:48:14.403 回答