【发布时间】:2013-07-23 00:42:49
【问题描述】:
当我使用 file_sink(在 boost::iostreams 中)和 fork() 子进程时,我观察到一些奇怪的行为。
孩子继续相同的代码库,即没有exec() 调用,因为这是作为守护进程的一部分完成的。当然,我的完整代码完全守护进程,但我省略了那些对报告行为不必要的步骤。
以下代码是一个简化的示例,演示了该行为:
using namespace std;
namespace io = boost::iostreams;
void daemonize(std::ostream& log);
int main (int argc, char** argv)
{
io::stream_buffer<io::file_sink> logbuf;
std::ostream filelog(&logbuf);
//std::ofstream filelog;
// Step 1: open log
if (argc > 1)
{
//filelog.open(argv[1]);
logbuf.open(io::file_sink(argv[1]));
daemonize(filelog);
}
else
daemonize(std::cerr);
return EXIT_SUCCESS;
}
void daemonize(std::ostream& log)
{
log << "Log opened." << endl;
// Step 2: fork - parent stops, child continues
log.flush();
pid_t pid = fork(); // error checking omitted
if (pid > 0)
{
log << "Parent exiting." << endl;
exit(EXIT_SUCCESS);
}
assert(0 == pid); // child continues
// Step 3: write to log
sleep(1); // give parent process time to exit
log << "Hello World!" << endl;
}
如果我在没有参数的情况下运行它(例如,./a.out),以便它记录到 stderr,那么我会得到预期的输出:
Log opened.
Parent exiting.
Hello World!
但是,如果我执行./a.out temp; sleep 2; cat temp 之类的操作,我会得到:
Log opened.
Hello World!
因此,在 fork 之后,父级以某种方式不再写入文件。这是谜题 #1。
现在假设我只是将 io::stream_buffer<io::file_sink> logbuf; 移到 main 之外,以便它是一个全局变量。这样做并简单地运行 ./a.out 会得到与前一种情况相同的预期输出,但写入文件(例如 temp)现在会产生新的令人费解的行为:
Log opened.
Parent exiting.
Log opened.
Hello World!
写“日志已打开”的行。在fork() 之前,所以我不明白为什么它应该在输出中出现两次。 (我什至在fork() 之前放置了一个明确的flush(),以确保该输出行不是简单地缓冲,然后在fork() 期间复制缓冲区,后来两个副本最终都刷新到流中。 .) 这就是谜题 #2。
当然,如果我注释掉整个 fork() 进程(标记为“步骤 2”的整个部分),那么无论 logbuf 是否是全局的,它对于文件和 stderr 输出的行为都符合预期或本地到main()。
此外,如果我将filelog 切换为ofstream 而不是stream_buffer<file_sink>(请参阅main() 中注释掉的行),那么它对于文件和stderr 输出的行为也都符合预期,并且无论filelog/logbuf 对于main() 是全局的还是本地的。
所以看起来确实是file_sink 和fork() 之间的交互产生了这些奇怪的行为......如果有人对可能导致这些问题的原因有任何想法,我将不胜感激!
【问题讨论】:
标签: c++ boost file-io process fork