关于Linux和Windows的内核区别
关于Linux和Windows的口水站已经很多了。本文企图从技术角度来比较下2个主流操作系统的异同。偏重于内核部分。
一、二者区别:
我觉得二者最大的区别在于Windows是个商业软件,而Linux是开源软件。商业软件的好处是可以集中一大批人力物力做一件事情。容易统一,兼容(因为客户需求)。而开源的好处在于灵活,开放。
在下面的比较中,我一般先介绍下Windows的,然后再介绍Linux的。
1、观念:商业 VS 开源
Windows是个商业软件,它的源码是保密的. 当然,其他非MS的人也还是有机会看到源码的. 如果你和MS 签订一个NDA(NON DISCLOSURE AGREEMENT),那么你也有可能拿到Windows代码.
言规正传,我觉得商业也还是有好处的。比如兼容性好,我以前用WDM写一个驱动,最多改下编译选项就可以在WIN 98, WIN 2K, WIN XP下运行。十分方便。而如果换成Linux,那么你只好祈祷不同的内核版本之间没改那些你用到的头文件,函数接口。否则就要改代码了。
同时,开源的好处是适合学习,十分灵活。我觉得Linux十分适合学校,学生。因为开源,当你发现不明白的地方的时候,可以直接去看源码(还记得RTFS? )。看不懂还可以到论坛上问。而对于Windows,你想了解它的内部机制就只好GOOGLE,然后祈祷了。比较好的一个资源是MSDN下面的一个杂志,其中有一个主题叫UNDER THE HOOD, 或者搜搜 BUGSLAYER 也可以。这2个专题的作者Matt Pietrek和John Robbins都是大牛级的人物。
来到美国之后,我渐渐明白了。HOOD 在这里应该理解为汽车的引擎盖。在美国,汽车是很普遍的。如果你开车,但是从来没打开过引擎盖,那么说明你只会用,而不了解汽车内部。那么如果你打开盖子看看呢?就可以看到很多内部细节,比如发动机啥的了。
在美国这个汽车王国,很多软件术语和汽车有关,因为人们日常生活中对汽车也很了解。比如“引擎”这个词,以前玩3D游戏的时候,常会看到介绍说,本游戏采用了最新的3D引擎。啥意思呢?就是游戏最核心的部分(汽车引擎)已经升级了。不是只把外面的人物形象改了下而已。
另外,开源软件也经常用汽车来类比。开源意外着你买了车(软件)后,可以随便拿到一个修理厂去修。也就是什么人都可以改,只要他懂。而COPY RIGHT 软件呢,就是你买了车,但是引擎盖子是锁着的,坏了只能去生产厂家修,其他人修不了。如果万一生产厂家不想修或者不会修呢?那你就只能认命了。
扯得有点远了,打住。
1.1、发布:2进制 VS 源码
这里主要讨论下Windows和Linux在发布程序采用的不同的形式和观念,这些和前面的商业还是开源的基本观念是联系在一起的。
在Windows 世界,安装程序几乎全部都是以二进制形式发布的。也就是说,用户下载了一个程序,然后双击,一路NEXT,NEXT,NEXT就可以了。这个方法很适合初学者。在Linux世界也有类似的机制,比如YUM, APT-GET 等。不过YUM和APT-GET都是比较晚才出现的,在那之前,在Linux世界安装程序要更麻烦些。
有的时候,Linux的YUM, APT-GET还不够用。比如有的人写的一个小软件,没有放到这些大的公共的库里面。这时,你就会发现他们一般提供一个或者一堆源文件,然后需要使用者自己下载,“编译”,安装。这也就是Linux世界常见的源代码发布的形式。
一开始的时候,十分不习惯Linux的这种发布形式。用惯了Windows的双击安装,总觉得Linux的安装很麻烦,又要自己./CONFIGURE, MAKE, MAKE INSTALL. 万一这个软件又依赖于其他的库,那么又要自己去找那些库,万一那些库又依赖其他的库...... 另外,各种库的版本也是一个问题,万一不兼容,那么又要找一个兼容的。
为什么Linux世界这么多源代码发布呢?为什么Windows世界流行2进制文件发布,而不是源代码呢?关于后者,很好解释,因为Windows那边很多源代码都是商业秘密,是不公开的。同时,Windows的程序用到的那些库在一般的系统里都装好了。所以2进制发布可行,也十分方便。
关于前一个问题,我觉得源代码发布的一个好处是可以在编译的时候进行一些优化和设置。比如同样的代码,在32或64位平台下编译的时候可以进行适当的优化。另外,用户也可以在编译的时候设置一些开关,这样在编译期间的优化一般要好于运行时间的优化。
不过源代码发布的一个坏处就是对使用者要求较高。如果运行configue,make命令顺利的话还好。如果万一不顺利,要自己改下头文件啥的,无疑是一般的使用者无法做到的。另外库之间的依赖关系如果是人手工处理的话也十分麻烦。好在Linux世界后来有了YUM APT-GET之类的包管理系统。大多数软件都可以很方便的安装了。
2、进程及其创建 CreateProcess VS fork+execv
在Windows世界,创建进程最常用的WIN 32 API 是 CreateProcess以及相关函数。这个函数需要一堆参数(Windows API 的特点),不过很多参数可以简单的用NULL, TRUE OR FALSE来表示。另外,你直接告诉它要执行的是哪个文件。
到了Linux世界,我模糊的知道fork是用来创建一个新进程的。但是当我看fork的函数说明的时候,呆住了。因为fork不需要任何参数。习惯了 CreateProcess 的10来个参数,突然换成一个不要任何参数的函数,感觉很奇妙。一方面觉得似乎事情简单了很多,不用去把10来个参数的每个意思都搞明白。另外一方面又很疑惑,我怎么告诉它我要执行某个文件呢?
后来才知道,Linux中的进程的含义和Windows中是不一样的。Linux中的进程本身是可以执行的。而Windows中,进程只是表示一个资源的拥有体,是不能执行的。要执行的话,一定需要一个线程。这也部分解释了为什么CreateProcess中为啥一定要传入要执行的文件的名字。
而fork的含义是把进程本身CLONE一个新的出来。也就是说,FORK之后,父进程和子进程都执行同样的一段代码。如果想区分的话,可以根据FORK的返回值来区分。引用一段fork的说明:
On success, the PID of the child process is returned in the parent's thread of execution, and a 0 is returned in the child's thread of execution.
同时在Linux程序中,常见的写法如下: int pid;
pid = fork();
switch (pid)
{
case 0: //I am the child
;
case -1: //failed.
;
default: //I am the parent
}
为什么要这样设计呢?因为Linux的设计目标之一就是应用于服务器。这种情况下,一个SERVICE可能会启动很多进程(线程)来服务不同的CLIENT. 所以FORK设计成快速复制父进程。子进程直接使用父亲的地址空间,只有子进程加载一个新的可执行文件的时候才创建自己的地址空间。
这样节省了创建地址空间这个庞大的开销,使得Linux的进程创建十分快。不过实际上,这里的进程相对于Windows中的线程,所以同Windows中的线程创建相比,二者的开销应该差不多。
那么如何才能让新的进程加载一个可执行文件呢,这时就要用execv以及相关函数了。所以Linux中,代替CreateProcess()的函数是fork+execv