当我使用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!
因此,在分叉之后,父级不再以某种方式写入文件。这是谜题#1。
现在假设我只是移动io::stream_buffer<io::file_sink> logbuf;到 main 之外,以便它是一个全局变量。这样做并简单地运行./a.out会产生与前一种情况相同的预期输出,但是写入文件(例如 temp)现在会产生一个新的令人费解的行为:
Log opened.
Parent exiting.
Log opened.
Hello World!
写“日志已打开”的行。在之前,fork()所以我不明白为什么它应该在输出中出现两次。flush()(我什至在 the 之前放置了一个明确的内容fork(),以确保该输出行不只是被缓冲,然后缓冲区在 the 期间被复制fork(),后来两个副本最终都被刷新到流中......)所以这是谜题 #2。
当然,如果我注释掉整个fork()过程(标记为“步骤 2”的整个部分),那么它对于文件和stderr输出的行为都符合预期,无论logbuf是全局的还是本地的main()。
此外,如果我切换filelog为 anofstream而不是stream_buffer<file_sink>(请参阅 中注释掉的行main()),那么它对于文件和stderr输出的行为也都符合预期,无论filelog/logbuf是全局的还是本地的main().
因此,这似乎是这些奇怪行为之间的相互作用file_sink并fork()产生了这些奇怪的行为......如果有人对可能导致这些行为的原因有任何想法,我将不胜感激!