为了账号安全,请及时绑定邮箱和手机立即绑定

在Linux gcc中fork()的工作

在Linux gcc中fork()的工作

青春有我 2019-10-12 10:36:20
fork() 创建一个新进程,子进程从父进程的当前状态开始执行。这是我fork()在Linux中了解的事情。因此,相应地,以下代码:int main() {  printf("Hi");  fork();  return 0;}根据上述,只需打印一次“ Hi”即可。但是在用gcc编译的Linux上执行上述操作时,它会两次打印“ Hi” 。有人可以向我解释使用中实际发生了什么fork(),如果我理解fork()正确的工作原理吗?
查看完整描述

3 回答

?
狐的传说

TA贡献1804条经验 获得超3个赞

当您将某些内容打印到“标准输出”标准输出(通常是计算机监视器,尽管您可以将其重定向到文件)时,它最初会存储在临时缓冲区中。

分叉的两面都继承未刷新的缓冲区,因此,当分叉的每一面触及return语句并结束时,都会刷新两次。

在分叉之前,应该fflush(stdout);先刷新缓冲区,以使子代不会继承该缓冲区。

屏幕上的stdout(而不是将其重定向到文件时)实际上是由行尾缓冲的,因此,如果您这样做了printf("Hi\n");,则不会有此问题,因为它会刷新缓冲区本身。


查看完整回答
反对 回复 2019-10-12
?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

printf("Hi");并不会立即在屏幕上显示“ Hi”一词。它所做的是在stdout缓冲区中填充单词“ Hi”,一旦缓冲区“被刷新”,该字词就会显示出来。在这种情况下,stdout指向您的显示器(假定)。在这种情况下,缓冲区将在缓冲区已满,强制您刷新或(最常见)打印换行符(“ \ n”)时刷新。由于在fork()调用时缓冲区仍然充满,因此父进程和子进程都继承该缓冲区,因此在刷新缓冲区时,它们都将打印出“ Hi”。如果fflush(stout);在调用fork之前先调用,它应该可以工作:


int main() {

  printf("Hi");

  fflush(stdout);

  fork();

  return 0;

}

另外,正如我所说,如果您在其中包含换行符,printf那么它也应该可以正常工作:


int main() {

  printf("Hi\n");

  fork();

  return 0;

}


查看完整回答
反对 回复 2019-10-12
?
温温酱

TA贡献1752条经验 获得超4个赞

通常,在fork()两侧的库中使用开放的句柄/对象是非常不安全的。


这包括C标准库。


fork()使两个进程合二为一,没有库可以检测到它的发生。因此,如果两个进程继续使用相同的文件描述符/套接字等运行,它们现在将具有不同的状态,但共享相同的文件句柄(从技术上讲,它们具有副本,但具有相同的基础文件)。这使坏事发生。


fork()导致此问题的情况的示例


stdio,例如tty输入/输出,管道,光盘文件

数据库客户端库使用的套接字

服务器进程使用的套接字-当为一个套接字提供服务的孩子碰巧继承了文件的句柄时,可能会产生奇怪的效果-正确地进行这种编程是很棘手的,请参阅Apache的源代码示例。

在一般情况下如何解决此问题:


要么


a)在fork()之后,可能立即在同一二进制文件上调用exec()(带有必要的参数以完成您打算做的任何工作)。这很容易。


b)分叉之后,请勿使用任何依赖于它们的现有打开的句柄或库对象(可以打开新的句柄);尽快完成工作,然后调用_exit()(而不是exit())。不要从调用fork的子例程中返回,因为那样可能会导致调用C ++析构函数等,这可能会对父进程的文件描述符造成不良影响。这相当容易。


c)分叉之后,在让孩子继续之前,以某种方式清理所有物体并使它们都处于健全状态。例如,关闭基础文件描述符,而不刷新父缓冲区中重复的缓冲区中的数据。这很棘手。


c)大约是Apache所做的。

查看完整回答
反对 回复 2019-10-12
  • 3 回答
  • 0 关注
  • 547 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信