使用popen()读取时为什么会出现内置延迟?

时间:2015-04-21 14:22:22

标签: c++ file c++11 popen nonblocking

我通过popen()执行长时间运行(通常是被阻止的)命令:" ls -R /"

问题:popen()读入你提供的缓冲区,它似乎试图在返回之前填充ENTIRE缓冲区。这会导致它经常阻塞(如果你的缓冲区很大)。

解决方案似乎是使基础fd无阻塞。当我这样做时,popen()仍会阻塞,通常每次约1秒。为什么会这样?

这是我的代码。确保使用-std = c ++ 11编译:

#include <cstdio>
#include <iostream>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>

static constexpr size_t SIZE = 65536;

struct Time
{
   friend std::ostream &operator<<(std::ostream &os, Time const &t)
   {
      (void)t;

      timeval tv;
      gettimeofday(&tv, nullptr);

      os << tv.tv_sec << "." << std::fixed << tv.tv_usec << " ";
      return os;
   }
};

int main()
{
   FILE *file;
   file = popen("ls -R /", "r");
   if(!file)
   {
      std::cerr << "Could not open app: " << errno;
      return -1;
   }

   // Make it non-blocking
   int fd = fileno(file);
   fcntl(fd, F_SETFL, O_NONBLOCK);

   char buffer[SIZE];
   Time t;
   while(true)
   {
      int rc = fread(buffer, 1, SIZE, file);
      if(rc <= 0)
      {
         if(EAGAIN == errno)
         {
            usleep(10);
            continue;
         }
         std::cerr << t << "Error reading: " << errno << std::endl;
         break;
      }
      std::cerr << t << "Read " << rc << std::endl;
   }
   pclose(file);
   return 0;
}

输出(注意它们相距约1秒,即使fd是非阻塞的,我在循环中只有10mS的暂停):

1429625100.983786 Read 4096
1429625101.745369 Read 4096
1429625102.426967 Read 4096
1429625103.185273 Read 4096
1429625103.834241 Read 4096
1429625104.512131 Read 4096
1429625105.188010 Read 4096
1429625105.942257 Read 4096
1429625106.642877 Read 4096

1 个答案:

答案 0 :(得分:5)

首先,您应该使用read而不是fread。 stdio函数在OS之外有自己的缓冲层,因此它们甚至可以阻止非阻塞文件描述符。使用read来避免这种情况。

其次,您需要阻止ls缓冲其输出。 {4}}是在stdout连接到TTY时使用行缓冲,在连接到管道或重定向到文件时使用完全缓冲。完全缓冲意味着仅在4KB缓冲区填满时才刷新输出,而不是每次输出换行时刷新。

您可以使用stdbuf覆盖此行为。请注意,它仅适用于使用C流并链接到glibc的程序。这是大多数程序,但不是全部。

popen("stdbuf -oL ls -R /", "r");
相关问题