fork到底做了什么?

       在 linux 中 fork 是一个非常重要的函数,它从一个已经存在的进程中创建出了一个新进程,这个新进程称为子进程,而原进程称为父进程。

为什么 fork 函数看起来像是返回了两次?

       对于很多初学者来说,这是最不能理解的。的确,这只能说是看起来像,因为从汇编的层面就已经决定了栈的压栈和出栈是成对出现的,一个函数永远只会返回一次。fork 函数的返回两次只是一个表面现象,那本质上是怎么回事呢?

       fork 函数调用之后,就会为子进程创建一个新的 pcb,子进程就会完全拷贝父进程的地址空间,包括堆、栈、代码段都被拷贝了过来,因此,子进程的栈是从父进程那拷贝过来的,那么它和父进程的函数栈帧就是完全相同的,在函数执行完毕后,父子进程在各自的函数栈帧中返回,造成了我们所看到的两次返回的假象。

fork 后父子进程谁先执行?

       通常情况下,我们总是用 sleep 等操作来保证另一个进程先执行,但父子进程谁先执行并不是不可预测的。从linux内核2.6.32开始,在默认情况下,父进程将成为fork之后优先调度的对象。采取这种策略的原因很简单:fork是父进程发起的调用,因此fork之后,父进程在CPU中处于活跃的状态,并且其内存管理信息也被置于硬件内存单元的转译后备缓冲器(TLB),所以先调度父进程无论从减少上下文切换、CPU让出等方面都可以提高性能。linux内核从2.6.24开始,内核采用完全公平调度(CFS),用户创建的普通进程,都采用CFS调度策略。对于CFS调度策略,内核提供的控制选项默认是0,表示父进程优先获得调度,如果该值被改为1,子进程会优先获得调度。

       但POSIX标准和linux都没有保证会优先调度父进程,因此在应用中,我们不能对父子进程的执行顺序做任何假设。如果确实需要父子进程的某一特定执行顺序,那么还是得需要进程间的同步手段。

写时拷贝

       fork 函数最常规的用法:一个父进程希望复制自己,使父子进程同时执行不同的代码,这时就需要在子进程中调用 exec 族函数来使子进程丢弃当前拷贝父进程的所有代码段,并创建新的代码段和堆栈。在这种情况下如果还让子进程在一开始就完全拷贝父进程的地址空间,显然是一种很不明智的做法,因此linux引入了写时拷贝(copy-on-write)的方法。

       写时拷贝,从字面上来理解就是写的时候才拷贝,具体来说就是指子进程 pcb 的页表指向与父进程相同的物理内存页,这样就只拷贝父进程的页表项即可,但这个时候内存中的所有内容都是只读的,如果父子进程都对内存进行只读操作,那么这个状态就可以一直保持下去,一旦有一方想要对内存进行写操作,就会引发缺页异常,此时,内核才会为子进程申请一个新的物理页,并将原物理页中的内容真正的复制到新的物理页中,让父子进程真正拥有各自的物理内存页,此时他们各自内存中的内容就是可写的了。

   

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值