exec 函数可以执行一个进程,是 Run-Time Library 里面的特性,标准上并没有给出什么硬性规定,因此各个操作系统平台上就可以有五花八门的实现了,本文介绍 linux 上的实现鸠占鹊巢和 windows 下的实现功成身退。
在 linux 上,著名的 exec 函数族可谓是无人不晓,它最著名的特性就是可以实现替换当前进程的地址空间,因此你如果在一个进程当中执行了 exec ,那 就不要指望 exec 返回执行 exec 调用点后面的操作了。 exec 并不负责创建进程,他只负责替换地址空间,负责创建进程的是由另一个系统调用来承担,就 是 fork ,所以在 linux 中典型的创建新的执行映像的方式就是 fork+exec ,当然在你不想要当前进程的情况下也可以直接 exec 。而在 windows 下, exec 却是简单的 CreateProcess+ExitProcess 的封装,在 windows 下一切最终都要落实到 win32API 的,因此 Win32API 相当于多出来的一个层次,而不像 linux 直接进行系统调用完事,从这二者的实现我们可以看出什么端倪吗?
凡是用过 unix 的都知道 unix 的哲学,每次只做并且做好一件事情这个终极哲学,你看 fork ,连参数都没有,就是创建进程,再看 exec ,根本不管进 程的事,就是替换地址空间,只有这样,把如何组合的策略交给用户,用户才可以获得最大的自由,从而开发出丰富的应用程序。 linux 本身是一点策略都不提 供的,它不会帮你做你认为会省力的工作,也就是说,只要是你可以完成的工作它就绝对不插手,比如在不影响当前进程的情况下启动另一个进程, linux 认为 没有必要直接提供这样一个系统调用,这里的用户指的是所有系统调用层上面的用户,包括库的设计者。库的设计者或者程序员用户可以随意组合这些现有的系统调 用接口,封装成更加方便的接口,只可惜操作系统并不会帮你,举个例子, fork+exec 就有一个封装函数,叫做 system 。我们反过来看看 windows 的策略, windows 为了开发者可以最快的进行开发,另外加上它的内核和用户空间是比较同步的进行开发的,联系非常紧密,两个空间非常的 了解对方,因此 windows 往往会直接提供一些策略,这就增加了操作系统和开发者之间的耦合度,不像在 linux 下开发,系统调用接口很稳定,一般不怎 么变化,内核空间和用户空间只需要通过这个稳定的接口层通信就可以了,而且彼此完全可以不了解对方,正如我前一篇文章所说,这种特性带来的就是术业有专攻
的 庞大社区的支持,力量非常强大。既然 windows 提供了一些用户策略,那么它就明显不适合 unix 哲学同样是创建进程 windows 的 CreateProcess 函数几乎为用户做完了全部的事情,包括创建进程,映射程序映像,这就是和 linux 的最大不同了。当然并不能说 windows 不好,它也有自己的哲学,作为一个产品,它的开发者深知自己的产品,另外不管是内核还是用户空间策略都是产品的一部分,因此就没有必要区分那么清了,它的 开发者通过会议的方式进行协作,因此他们之间的通信就不一定要局限在系统调用接口了。
可能有些人会认为 windows 的 exec 的方式更加干净,确实有那么一点意思, linux 的方式有些罗嗦也有那么一点意思,可是用户真的需要这种所谓的 干净吗?那要看用户是什么样的人了,信奉不同哲学的人往往会因为一点小的分歧大开杀戒。事实上 linux 的 exec 和 windows 的 exec 根本就不是 一个层面上的,在 linux 中, exec 只是 “ 创建进程 ” 这件事情的 “ 一部分 ” ,而在 windows 下, exec 却包含了 “ 创建进程 ” 这件事情,但是还不 止这件事情,另外还包括 “ 退出当前进程 ” 这件事情,用一句俗语区分它们就是,在 linux 中, exec 的作用是 “ 鸠占鹊巢 ” ,在 windows 中, exec 的作用一个是 “ 功成 ” 另一个是 “ 身退 ” 。这里就可以看出 linux 和 windows 的设计粒度,显然 linux 更加细化一些,细化的东西组合 起来往往会出现规模效应,功能会很强大,而粗化的东西在扩展的时候功能会受到限制,比如 linux 可以很简单的实现 “ 功成 ”+“ 身退 ” ,就是 fork+exec+exit ,其实就是三个只做一件事的系统调用之组合,反过来要在 windows 下实现 “ 鸠占鹊巢 ” 就不容易了,因此 windows 中 最细的东西都要比这个 “ 鸠占鹊巢 ” 要粗,于是就受到了限制。这样的话,在 linux 中就可以在保持 pid 不变的情况下重新启动自身,而在 windows 下 这个需求就很难了。
虽然同样都是 exec 函数,在 linux 和 windows 中的地位却大不相同,当然其位置也不同,在 linux 中可以很安心的将之放到系统调用门口,因为 它就做一件事,不会有任何副作用,但是在 windows 下,它的存在纯粹是为了兼容标准 C ,于是就要用 Win32API 来封装 exec 函数,而 Win32API 中却没有替换地址空间这么细粒度的操作,于是乎只好仅仅实现语义,于是最终就有了 CreateProcess+ExitProcess 的 组合,有人可能会问这不也是一个组合吗?是的,这是一个组合,但是这是由只做一件事的函数作为元素组合的吗?显然不是。