作者一直试图从最基本的原理去理解(甚至尝试设计)一个服务器的架构,并为此提出了一些问题。此外,作者还学习了很多有关异步I/O的知识。从几个方面学习了vfork()的用法。本文纯属记录。但是,与其他资料中的大段代码不同,本文使用更多的文字和布局来尽可能清楚地说明。本文地址:https://segmentfault.com/a/1190000010411198参考:linux网络编程之socket(四):使用fork并发处理多个客户端请求和点对点通信p2pfork和vfork的区别fork和vforkfork()应该说是UNIX和类UNIX系统中最古老最原始的创建子进程的系统调用。甚至可以说,fork不仅仅是子进程的创建,更多的是创建进程的主要手段。除init进程外,所有进程均派生自init。很多进程都可以是init或bash的子进程,但在这种情况下,我们不再将它们视为子进程。fork()一句话,fork()的方法就是复制父进程的上下文,然后从父进程中分离出来。事实上,大多数程序员可能会使用fork()创建一个子进程,然后父子进程使用管道进行通信。fork复制的上下文与父进程的关系在实际使用中,确实按照上面的说法,fork()之后的子进程是对父进程的复制关系。也就是说,子进程对大部分变量的操作不会影响到父进程。但是需要注意的是文件描述符等与操作系统层面相关的全局资源:这些只是上下文在内存中的位置,但是它们在底层所指向的资源是共享的,所以需要操作时要注意。但实际上,这也是父子进程之间使用管道进行通信的原理基础。fork复制上下文的原理我被问到一个问题:“调用fork()时,必须复制上下文,在大进程中调用会不会效率低下?”Linux作为开源运动的杰作,显然没有那么傻。实际上,在调用fork之后,Linux并不会立即复制上下文,而是在需要的时候才会复制。所以对于效率和内存的使用我们大可放心。但是,有一个例外,稍后将介绍。至于linux是如何实现这个过程的原理,以下是我的推测。如有不妥,请读者指出,现代操作系统依赖于一项非常重要的技术,即内存映射,这需要硬件CPU支持MMU。进程看到的内存地址实际上并不是RAM中的实际内存偏移量。操作系统将进程实际使用的内存地址值映射到实际的硬件RAM中。如果系统强行或不小心访问了映射表中未注册的地址值范围,硬件MMU模块将发出低级硬件中断。操作系统监听这个中断就知道发生了非法的内存访问,也就是段错误。有像MMU这样强大的东西,为什么不使用它呢?fork之后,操作系统可以收起父进程未被子进程使用的内存内容,不急于在内存中创建副本。如果在子进程的处理过程中出现越界访问,操作系统完全可以判断该内存是否为父进程的内存内容。如果不是,则抛出段错误;如果是,则复制内存内容并创建内存映射-这是子进程实际复制父进程内容的时候。至于节目环节?即只读内容,根本不需要拷贝,只需要共享同一块实际内存即可。所以如上所说,我们不用担心子进程的内存浪费问题。但例外的是:比如进程Afork子进程B,子进程B实际占用内存很小,所以实际增加的内存并不多。但是一旦进程A退出,进程A占用的内存就无法回收了,因为操作系统怎么知道进程B是否应该使用A的内存内容呢?也就是说,调用fork()的进程还是会尽量节省内存,或者尽量在用完就归还。vfork()首先:vfork和fork最大的区别是:子进程与父进程共享相同的内存空间。也就是说,子进程对所有变量的操作都会直接影响到父进程——这也是很多人害怕vfork的原因。为了避免这样的操作,vfork与fork还有一个额外的区别:其次:vfork之后得到的子进程可以保证在调用exit或者exec系列调用之前不会执行父进程。这是一个非常重要的特性。以上两个特点引出了vfork的应用场景。Shell在这里调用系统命令主要是应用了上面提到的第二个特性。详见我之前的文章。跨进程计数这个源于最近看到的一段代码中的一个函数,就是统计每个进程中的一个操作(也就是++)。这时候vfork就派上用场了。但是注意,因为上面提到的vfork的隐患,所以这个比较考验编程艺术的。。。呃,本来想多写的,但其实这样的场景怎么设计实现还是要搞定的.没有仔细想过……所以先把自己的想法放在这里,继续努力学习。(什么?进程互斥?不用担心,++i是原子操作,只用一条机器指令就可以完成,不考虑互斥)
