fork()与写时复制(Copy-On-Write, COW)
概念
fork函数:
- 返回子进程的pid给父进程:父进程没有函数可以获取所有子进程的pid。
- 返回0给子进程:子进程可以通过getppid获取父进程的pid;而PID=0是内核交换进程,不可能是子进程,因此返回0作为标识,与父进程区分。
COW技术:
- Linux的fork()使用写时复制技术,父子进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间(也就是说,两者的虚拟空间不同,但其对应的物理空间相同)。
- fork()的实际开销就是复制父进程的页表以及给子进程创建惟一的进程描述符。
- 当父子任一进程试图修改数据段、堆栈,才会为修改的区域(通常为虚存中一页)制作副本。
关于持有副本的是父进程还是子进程,参考:https://zhoujianshi.github.io/articles/2017/fork()%E5%90%8Ecopy%20on%20write%E7%9A%84%E4%B8%80%E4%BA%9B%E7%89%B9%E6%80%A7/index.html
COW分析
由于fork()的子进程经常调用exec()函数,且exec()会用新的程序替换当前的正文段、数据段、堆栈(也就是分配新的物理空间),此时子进程不需要父进程内存的副本。因此采用COW技术提高了效率。
其他
- fork之后内核会通过将子进程放在队列的前面(取决于调度算法),以让子进程先执行,以免父进程执行导致写时复制,而后子进程执行exec系统调用,因无意义的复制而造成效率的下降。
- vfork():
- 子进程目的是exec一个新程序
- 直接共享了父进程的虚拟空间
- 保证子进程先运行,直到调用exec或exit,父进程才可能被调度。
- stl::string:有些版本的stl采用浅拷贝,只拷贝指针,只有在内容被修改的时候, 才真正分配了新的内存并copy。也就是COW技术。
- 父子进程共享文件偏移量