我通过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
答案 0 :(得分:5)
首先,您应该使用read
而不是fread
。 stdio函数在OS之外有自己的缓冲层,因此它们甚至可以阻止非阻塞文件描述符。使用read
来避免这种情况。
其次,您需要阻止ls
缓冲其输出。 {4}}是在stdout连接到TTY时使用行缓冲,在连接到管道或重定向到文件时使用完全缓冲。完全缓冲意味着仅在4KB缓冲区填满时才刷新输出,而不是每次输出换行时刷新。
您可以使用stdbuf
覆盖此行为。请注意,它仅适用于使用C流并链接到glibc的程序。这是大多数程序,但不是全部。
popen("stdbuf -oL ls -R /", "r");