fork()创建一个新进程,子进程从父进程的当前状态开始执行.
这是我fork()在Linux中所知道的.
所以,相应的代码如下:
int main() {
printf("Hi");
fork();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
需要按照上述方式仅打印一次"Hi".
但是在使用gcc编译的Linux中执行上述操作时,它会打印两次 "Hi" .
有人可以向我解释实际使用中发生了什么,fork()以及我是否理解了fork()正确的工作?
Pau*_*lin 36
(在用户@Jack的注释中加入一些解释)当您将某些内容打印到"标准输出"标准输出(通常是计算机监视器,尽管您可以将其重定向到文件)时,它最初会存储在临时缓冲区中.
fork的两端都继承了未刷新的缓冲区,因此当fork的每一侧命中return语句并结束时,它会被刷新两次.
在fork之前,你应该fflush(stdout);刷新缓冲区,以便子进程不继承它.
stdout到屏幕(而不是当你将它重定向到文件时)实际上是由行结束缓冲的,所以如果你做了,printf("Hi\n");你就不会遇到这个问题,因为它会刷新缓冲区本身.
Ste*_*hen 22
printf("Hi");实际上并没有立即在屏幕上打印"Hi"字样.它所做的是stdout用缓冲区"刷新" 填充缓冲区,然后在缓冲区被"刷新"后显示.在这种情况下,stdout指向您的显示器(假设).在这种情况下,缓冲区将在它满时刷新,强制刷新时刷新,或者(最常见)在打印换行符("\n")时刷新.由于缓冲区在fork()调用时仍然是满的,因此父进程和子进程都继承它,因此它们在刷新缓冲区时都会打印出"Hi".如果你fflush(stout);在调用fork之前调用它应该工作:
int main() {
printf("Hi");
fflush(stdout);
fork();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
或者,正如我所说的,如果你在你的printf内容中包含换行符,那么它应该也可以使用:
int main() {
printf("Hi\n");
fork();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
通常,fork()两侧的库使用打开的句柄/对象是非常不安全的.
这包括C标准库.
fork()使两个进程成为一个进程,没有库可以检测到它发生.因此,如果两个进程继续使用相同的文件描述符/套接字等运行,它们现在具有不同的状态但共享相同的文件句柄(从技术上讲,它们具有副本,但是具有相同的底层文件).这会让糟糕的事情发生.
fork()导致此问题的示例
如何在一般情况下解决这个问题:
或
a)在fork()之后,立即调用exec(),可能在同一个二进制文件上(使用必要的参数来实现你打算做的任何工作).这很容易.
b)分叉后,不要使用任何现有的打开手柄或依赖它们的库对象(打开新的可以); 尽快完成你的工作,然后调用_exit()(而不是exit()).不要从调用fork的子例程返回,因为这可能会调用C++析构函数等,这可能会对父进程的文件描述符造成不良影响.这很容易.
c)在分叉之后,以某种方式清理所有物体并使它们在孩子继续之前处于理智状态.例如,关闭底层文件描述符而不刷新在父节点中重复的缓冲区中的数据.这很棘手.
c)几乎与Apache有关.