使用popen实现非阻塞管道?

21
我想使用 popen() 打开一个管道,并以非阻塞的方式访问它的'读'端口。如何实现?(我找到的例子都是阻塞/同步的)
3个回答

34

设置如下:

FILE *f = popen("./output", "r");
int d = fileno(f);
fcntl(d, F_SETFL, O_NONBLOCK);

现在您可以阅读:

ssize_t r = read(d, buf, count);
if (r == -1 && errno == EAGAIN)
    no data yet
else if (r > 0)
    received data
else
    pipe closed

完成后,进行清理:

pclose(f);

1
管道作为FILE指针,本质上是带缓冲的。如果直接使用文件描述符,有没有保证你不会错过已经被读入文件缓冲区的内容?或者只要不先调用fget/fread等函数,这个问题就可以得到保证吗? - stu

7

popen() 内部调用了 pipe()fork()dup2()(将子进程的文件描述符 0/1/2 指向管道)和 execve()。您考虑过使用这些函数吗?在这种情况下,您可以使用 fcntl() 将读取的管道设置为非阻塞。

更新: 这里有一个例子,仅供说明目的:

int read_pipe_for_command(const char **argv)
{
   int p[2];

   /* Create the pipe. */
   if (pipe(p))
   {
      return -1;
   }

   /* Set non-blocking on the readable end. */
   if (fcntl(p[0], F_SETFL, O_NONBLOCK))
   {
      close(p[0]);
      close(p[1]);
      return -1;
   }

   /* Create child process. */
   switch (fork())
   {
      case -1:
          close(p[0]);
          close(p[1]);
          return -1;
      case 0:
          /* We're the child process, close read end of pipe */
          close(p[0]);
          /* Make stdout into writable end */
          dup2(p[1], 1);
          /* Run program */
          execvp(*argv, argv);
          /* If we got this far there was an error... */
          perror(*argv);
          exit(-1);
      default:
          /* We're the parent process, close write end of pipe */
          close(p[1]);
          return p[0];
   }
}

这不应该是这样吗:如果(pipe(p) < 0)返回-1;? - Aktau
1
@Aktau,我更喜欢我的版本。系统调用将在成功时返回0。if语句测试非零值。 - asveikau
1
你是对的,你的版本也完全正确,我在想其他的系统调用! - Aktau
请注意:父进程和子进程的用法是相反的。kill() 函数返回 0 给子进程而非父进程。 - Luca Ceresoli
谢谢Luca,你说得对。我批准了这个编辑。(距离我的回答已经9年了!) - asveikau

2

我从未尝试过,但我不认为你不能使用fileno()获取文件描述符,使用fcntl()设置为非阻塞,并使用read()/write()。值得一试。


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