在UNIX中将创建进程分成了两部分:

  • fork():在新的地址空间中创建进程,读入可执行文件
  • exec():开始执行

fork():通过拷贝当前进程创建一个子进程,子进程和父进程的区别仅仅在于PID(每个进程唯一)和PPID(父进程的进程号)和某些资源统计量
exec():函数负责读取可执行文件并将其载入地址空间开始运行

fork()函数早期的时候是直接把所有的资源复制给新创建的进程,但是这种情况下数据不共享

为了优化,此后,Linux的fork()便使用写时拷贝(COW,copy-on-write)页来实现。内核并不复制整个进程地址空间,而是让父进程和子进程共享一个地址空间,而只要页面被共享,它们就不能被修改
无论父进程和子进程何时试图写一个共享的页面,就产生一个错误,这时内核就把这个页复制到一个新的页面中并标记为可写。原来的页面仍然是写保护的:当其它进程试图写入时,内核检查写进程是否是这个页面的唯一属主;如果是,它把这个页面标记为对这个进程是可写的。

fork()的实际开销就是复制父进程的页表以及给子进程创建唯一的进程描述符

页表的机制相信大家都比较熟络了吧,这是内核管理内存的一种方法

vfork()除了不拷贝父进程的页表项外,vfork()和fork()功能相同:子进程作为父进程的一个单独的线程在他的地址空间里运行,父进程被阻塞,直到子进程退出exit()或执行exec()。子进程不能向地址空间写入

总结就是

  1. fork ():子进程拷贝父进程的数据段,代码段
    vfork ( ):子进程与父进程共享数据段
  2. fork ()父子进程的执行次序不确定
    vfork 保证子进程先运行,在调用exec 或exit 之前与父进程数据是共享的,在它调用exec
    或exit 之后父进程才可能被调度运行。
  3. vfork ()保证子进程先运行,在她调用exec 或exit 之后父进程才可能被调度运行。如果在
    调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

参考:​​fork与vfork的区别​​