对 fork 函数的思考和理解

fork函数通过系统调用创建一个与原来进程几乎完全相同的进程。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中。相当于克隆了一个自己。

也就是说,原来的进程和fork出来的子进程的进程空间是完全一样的,包括 代码段,数据段,bss段,堆区,共享内存,栈区。如下图所示:



下面是几个关于子进程复制父进程的内存空间的一些细节:

  • 写时拷贝
    再进行内存空间的复制的时候,这里使用了copy-and-write(写时拷贝)的方式。简单的说,就是资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。(写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。)

  • fork 之后 关于文件的问题
    在Linux系统中,常常存在许多对文件的操作,fork()的执行将会对文件操作带来一些小麻烦。由于子进程会将父进程的大多数数据拷贝一份,这样在文件操作中就意味着子进程会获得父进程所有文件描述符的副本。(也就是文件句柄包含着当前文件的偏移量以及文件状态标志 都是完全相同的)


  • vfork 和 fork的区别
    vfork与fork相似,但是也有区别,具体区别归结为以下3点:

    1. fork() 子进程拷贝父进程的数据段,代码段. vfork() 完全和父进程共享内存,包括堆、BSS、初始化非0数据区等区域。

    2. fork() 父子进程的执行次序不确定. vfork():保证子进程先运行。

    为什么要使用 vfork?
    因为以前的fork当它创建一个子进程时,将会创建一个新的地址空间,并且拷贝父进程的资源,而往往在子进程中会执行exec调用,这样,前面的拷贝工作就是白费力气了,这种情况下,聪明的人就想出了vfork


  • 什么是僵尸进程?
    进程退出后,系统会把该进程的状态变成Zombie,然后给上一定的时间等着父进程来收集其退出信息,因为可能父进程正忙于别的事情来不及收集,所以,使用Zombie状态表示进程退出了,正在等待父进程收集信息中。

    # 查看僵尸进程
    ps -aux | grep ‘Z’
    
  • 如何防止僵尸进程的出现?
    子进程结束的时候,会产生一个sigchld信号。可以在父进程里面加入对这个信号的处理函数。在处理信号的函数里面调用wait 或 wait_pid 函数,回收子进程。

    这里有一个问题,就是如果有100个子进程需要收回,每一个子进程结束的时候,都会发送sigchild信号。很有可能是同时发送信号。但是信号机制,是不支持排队的。所以,在信号处理的函数里面要调用wait_pid + WNOHANG参数。

    WNOHANG参数
    表示即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。
    这样wait_pid就不会阻塞
    

    如果感兴趣的话,可以看这个 :
    https://www.cnblogs.com/wuchanming/p/4020463.html

  • 关于进程打开文件 以及 文件系统的补充

    一个进程去打开一个文件之后,会返回一个文件描述符。这个描述符对应了一个文件打开表。文件打开表对应着inode的结点。inode对应block。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容