正确的代码 - 带 popen 的非阻塞管道

Correct Code - Non-blocking pipe with popen

本文关键字:popen 管道 代码      更新时间:2023-10-16
关于

非阻塞管道有很多问题,但没有可以复制和粘贴(几乎不需要更正)和使用的代码示例。

我从这个线程中得到了想法和来源:使用popen的不堵塞管道?

但是如何使用它呢?在while周期? 请查看我的更改。真的需要使用errno == EAGAIN和额外的标头#include <cerrno>吗?如果需要,建议您拥有更好的版本:

    FILE *pipe;
    char buff[512];
    if ( !(pipe = popen( command.c_str(), "r")) ) return false;
    int d = fileno(pipe);   
    while ( true )
    {
        ssize_t r = read(d, buff, sizeof(buff));
        if (r == -1 && errno == EAGAIN) // really need errno? 
            continue;
        else if (r > 0)
            ptr_output->append(buff);       
        else
            break;
    }
    pclose(pipe);

是的。如果read调用返回错误值 ( -1并且errno设置为 EAGAIN ,则表示没有可用的数据,因此您continue循环重试。如果你摆脱了errno,错误将被有效地忽略,你的程序可能会崩溃。想象一下,如果你确实删除了它:当read返回-1,但是,比如说,错误是管道坏了(另一端关闭了它),你只会继续尝试循环并进入无限循环。坏主意。

在我的 Linux 多线程应用程序中 (Ubuntu 16.04, x86_64, Native POSIX Threads, libc.so.6 version Ubuntu GLIBC 2.23-0ubuntu9)此代码

int fd = fileno(pipe);
fcntl(fd, F_SETFL, O_NONBLOCK);
while (true)
        {
            ssize_t r = read(fd, buff, sizeof(buff));
            if ((r == -1) && (errno == EAGAIN)) // really need errno?
                continue;
            else
            {
                if (r > 0)
                    result += buff;
                else
                    break;
            }
        }

即使有fctnl(...)原因也会失败,因为不仅命令输出,而且经常读取来自RAM的许多其他任意值。我认为这是由于代码的线程不安全

(示例:按 20 个线程并行执行 echo 3 个值 10、30 和 40,read值并仅显示最后一个值。输出: 40 QIh MIh 40 40 30...)

此答案中的另一个代码工作正常fgets因为实现是线程安全的

while (!feof(pipe))
{
    if (fgets(buff, sizeof(buff), pipe) != NULL)
    result += buff;
}