当我使用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()
产生了这些奇怪的行为......如果有人对可能导致这些行为的原因有任何想法,我将不胜感激!