3.1 项目知识准备
3.1.1 开放源码、编译器与可执行文件
Linux上面的软件几乎都经过了GPL的授权,所以这些软件均可提供原始程序代码,并且用户可以自行修改该程序源码,以符合个人的需求,这就是开放源码的优点。不过,到底什么是开放源码?这些程序代码到底是什么?Linux上面可以运行的相关软件文件与开放源码之间是如何转换的?不同版本的Linux之间能不能使用同一个运行文件?或者该运行文件需要由原始程序代码的部分重新进行转换吗?要回答这些问题,并不难,下面将一一进行解答。
在讨论程序代码是什么之前,我们先来谈论一下什么是可执行文件。在Linux系统上面,一个文件能不能被运行取决于有没有可运行的权限(具有x permission),不过,Linux系统上的可执行文件其实是二进制文件(二进制程序),例如/usr/bin/passwd、/bin/touch等文件。
那么shell script是不是可执行文件呢?答案是否定的。shell script只是利用shell(例如bash)这个程序的功能进行一些判断,除了bash提供的功能外,最终运行的仍是调用一些已经编译好的二进制程序。当然,bash本身也是一个二进制程序。
使用file命令能够测试一个文件是否为binary文件。具体命令如下所示。
(1)测试/bin/bash
[root@www~]# file/bin/bash /bin/bash: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked(uses shared libs), for GNU/Linux 2.6.9, stripped
(2)测试/etc/init.d/syslog
[root@www~]# file/etc/init.d/syslog /etc/init.d/syslog: Bourne-Again shell script text executable
如果是binary而且是可执行文件,就会显示执行文件类别(ELF 32-bit LSB executable),同时会说明是否使用动态函数库(shared libs)。而如果是一般的script,那么就会显示出text executables之类的字样。
事实上,syslog的数据显示出“Bourne-Again...”这一行,是因为script上面第一行声明“#! /bin/bash”的缘故,如果将script的第一行去掉,那么不管/etc/init.d/syslog的权限怎样,显示的都是ASCII文件的信息。
既然Linux操作系统真正识别的是二进制程序,那么该如何制作binary程序呢?首先使用vim来进行程序的撰写,写完的程序就是所谓的原始程序代码。其次,在完成这个源码文件的编写之后,将这个文件编译成操作系统“看得懂”的二进制程序。举个例子来说。在Linux上面最标准的程序语言为C,所以使用C语言进行原始程序代码的书写,写完之后,以Linux上标准的C语言编译器gcc来编译,就可以制作一个可以运行的二进制程序了。
开放源码、编译器、可执行文件可以总结如下。
• 开放源码:即程序代码,写给用户看的程序语言,但机器并不认识,所以无法运行。
• 编译器:将程序代码编译成为机器看得懂的语言,类似翻译者的角色。
• 可执行文件:经过编译器变成二进制程序后,机器看得懂可以直接运行的文件。
3.1.2 make与configure
事实上,使用类似gcc的编译器来进行编译的过程并不简单,因为一套软件并不会仅有一个程序,而是有大量的程序代码文件。所以除了主程序与副程序需要写上编译过程的命令外,还需要写上最终的链接程序。但是类似WWW服务器软件(例如Apache)或核心的源码这种动辄数百MB的数据量,编译命令的量过于庞大。这个时候,我们就可以使用make这个命令的相关功能来进行编译过程的命令简化了。
当运行make时,make会在当前的目录下搜寻Makefile(or makefile)这个文件,而Makefile里面记录了源码如何编译的详细信息。make会自动地判别源码是否已经改变,从而自动升级执行文件,所以,make是相当好用的一个辅助工具。
make是一个程序,会去找Makefile,那么Makefile应该怎么撰写呢?通常软件开发商都会写一个检测程序来检测使用者的操作环境,以及该操作环境是否有软件开发商所需要的其他功能,该检测程序检测完毕后,就会主动地创建这个Makefile的规则文件。通常这个检测程序的文件名为configure或者是config。
为什么要检测操作环境呢?因为不同版本的核心所使用的系统调用可能不相同,而且每个软件所需要的相关的函数库也不相同。同时,软件开发商不会仅针对Linux开发,而是会针对整个Unix-Like做开发,所以也必须要检测该操作系统平台有没有提供合适的编译器才行。一般来说,检测程序所检测的数据有如下几种类型:
• 是否有适合的编译器可以编译本软件的程序代码;
• 是否已经存在本软件所需要的函数库,或其他需要的相关软件;
• 操作系统平台是否适合本软件,包括Linux的核心版本;
• 内核的头定义文件(header include)是否存在(驱动程序必须要进行的检测)。
由于不同的Linux发行版本的函数库文件的路径、函数库的文件名定义、默认安装的编译器以及内核的版本都不相同,因此理论上,在CentOS 5.x上面编译出二进制程序后,然后在SuSE上面运行,这个动作通常是不可能成功的。因为调用的目标函数库位置可能不同,内核版本更不可能相同,所以能够运行的概率微乎其微。当同一套软件在不同的平台上面运行时,必须要重复编译。
3.1.3 Tarball软件
Tarball文件是将软件的所有源码文件先以tar打包,然后再以压缩技术来压缩而得到的文件,其中最常见的就是以gzip来压缩。因为利用了tar与gzip的功能,所以tarball文件一般的扩展名会写成*.tar.gz或者是简写为*.tgz。不过,近来由于bzip2的压缩率较佳,所以Tarball也有用bzip2的压缩技术来压缩的,这时文件名就会写成*.tar.bz2。
Tarball是一个软件包,将其解压缩之后,里面的文件通常会有:
• 原始程序代码文件;
• 检测程序文件(可能是configure或config等文件名);
• 本软件的简易说明与安装说明(Install或Readme)。
其中最重要的是Install或者Readme这两个文件,通常只要能够看明白这两个文件,Tarball软件的安装就非常容易进行了。
3.1.4 安装与升级软件
软件升级的主要原因有以下几种:
• 需要新的功能,但旧版软件并没有这种功能;
• 旧版软件可能有安全隐患;
• 旧版软件运行效率不高,或者运行的能力不能让管理者满足。
在上面的需求当中,尤其需要注意的是第二点,当一个软件有安全隐患时,千万不要怀疑,最好的办法就是立即升级软件,否则造成网络危机,问题就很难解决。升级的方法可以分为两大类:
• 直接以源码通过编译来安装与升级;
• 直接以编译好的二进制程序来安装与升级。
上面第一点很简单,就是直接以Tarball进行检测、编译、安装与配置等操作来升级。不过,这样的操作虽然让使用者在安装过程当中具有很高的选择性,但是比较麻烦。如果Linux distribution厂商能够针对自己的操作平台先进行编译等过程,再将编译好的二进制程序发布,那么由于自己的系统与该Linux distribution的环境是相同的,所以厂商发布的二进制程序就可以在自己的机器上面直接安装,省略了检测与编译等繁杂的过程。
这种预先编译好程序的机制存在于很多distribution版本中。如由Red Hat系统(含Fedora/CentOS系列)发展的RPM软件管理机制与yum线上升级模式,Debian使用的dpkg软件管理机制与APT线上升级模式等。
Tarball安装的基本流程如下:
(1)将Tarball在厂商的网站上下载下来;
(2)将Tarball解压缩,生成源码文件;
(3)以gcc进行源码的编译(会产生目标文件object files);
(4)以gcc进行函数库、主程序和副程序的链接,形成主要的二进制文件;
(5)将(4)中的二进制文件以及相关的配置文件安装至主机上面。
步骤(3)和(4)可以通过make这个命令的功能来简化,所以整个步骤其实是很简单的,只不过需要在Linux系统里面至少有gcc以及make这两个软件即可。详细的过程以及所需软件在后面的章节将详细介绍。
3.1.5 RPM与DPKG
目前在Linux界最常见的软件安装方式有以下两种。
1)DPKG
这个机制最早是由Debian Linux社群开发出来的,通过DPKG机制,Debian提供的软件能够简单地安装,同时还能提供安装后的软件信息。衍生于Debian的其他Linux发行版本大多数也都使用dpkg机制来管理软件,包括B2D、Ubuntu等。
2)RPM
这个机制最早是由Red Hat公司开发出来的。后来由于软件很好用,很多发行版就使用这个机制来作为软件安装的管理方式,其中包括Fedora、CentOS、SuSE等知名的开发商。
如前所述,DPKG/RPM机制或多或少都会存在软件依赖性的问题,那该如何解决呢?由于每个软件文件都提供软件依赖性的检查,如果将依赖属性的数据做成列表,等到实际安装软件时,若存在依赖属性的软件时根据列表安装软件就可以解决依赖性问题。例如,安装A需要先安装B与C,而安装B则需要安装D与E,那么当要安装A时,通过依赖属性列表,管理机制自动去取得B、C、D、E来同时安装,就解决了软件依赖性的问题。
目前新的Linux开发商都提供这样的“线上升级”机制,通过这个机制,原版光盘只有第一次安装时用到,其他时候只要有网络,就能够取得开发商所提供的任何软件。在DPKG管理机制上开发出了APT线上升级机制,RPM则依开发商的不同,有Red Hat系统的YUM、SuSE系统的Yast Online Update(YOU)、Mandriva的urpmi软件等。线上升级如表3-1所示。
表3-1 各发行版本的线上升级
RHEL 6使用的软件管理机制为RPM机制,而用来作为线上升级的方式则为yum。
3.1.6 RPM与SRPM
RPM全名是RedHat Package Manager,简称为RPM。顾名思义,当初这个软件管理的机制是由Red Hat这家公司开发出来的。RPM是以一种数据库记录的方式来将所需要的软件安装到Linux系统的一套管理机制。
RPM最大的特点是将需要安装的软件先编译通过,并且打包成为RPM机制的包装文件,通过包装文件里面默认的数据库记录,记录软件安装时所必须具备的依赖属性软件。当软件安装在Linux主机时,RPM会先依照软件里面的数据库记录查询Linux主机的依赖属性软件是否满足,若满足则予以安装,若不满足则不予安装。
但是这也造成一些困扰。由于RPM文件是已经打包好的数据,里面的数据已经“编译完成”了,所以该软件文件只能安装在原来默认的硬件与操作系统版本中。也就是说,主机系统环境必须要与当初创建这个软件文件的主机环境相同才行。举例来说,rp-pppoe这个ADSL拨号软件,必须要在ppp软件存在的环境下才能进行安装。如果主机没有ppp软件,除非先安装ppp,否则rp-pppoe不能成功安装(当然也可以强制安装,但是通常都会出现一些问题)。
所以,通常不同的发行版本发布的RPM文件,并不能用在其他的distributions上。举例来说,Red Hat发布的RPM文件,通常无法直接在SuSE上面进行安装。更有甚者,相同发行版本的不同版本之间也无法互通,例如RHEL 4.x的RPM文件就无法直接套用在RHEL 5.x上。由此可知,使用RPM应注意以下问题:
• 软件文件安装的环境必须与打包时的环境需求一致或相当;
• 需要满足软件的依赖属性需求;
• 反安装时需要特别小心,最底层的软件不可先移除,否则可能造成整个系统出现问题。
如果想要安装其他发行版本提供的好用的RPM软件文件,这时就需要使用SRPM。
SRPM即Source RPM,也就是RPM文件里面含有源码。应特别注意的是,SRPM所提供的软件内容是源码,并没有经过编译。
通常SRPM的扩展名是以***.src.rpm格式命名。虽然SRPM提供的是源码,但是却不能使用Tarball直接来安装。这是因为SRPM虽然内容是源码,但是仍然含有该软件所需要的依赖性软件说明以及RPM文件所提供的数据。同时,SRPM与RPM不同之处是,SRPM也提供参数配置文件(configure与makefile)。所以,如果我们下载的是SRPM,那么要安装该软件时,需要完成以下两个步骤:
(1)将该软件以RPM管理的方式编译,此时SRPM会被编译成为RPM文件;
(2)将编译完成的RPM文件安装到Linux系统当中。
通常一个软件在发布的时候,都会同时发布该软件的RPM与SRPM。RPM文件必须在相同的Linux环境下才能够安装,而SRPM是源码的格式,可以通过修改SRPM内的参数配置文件,然后重新编译产生能适合Linux环境的RPM文件,这样就可以将该软件安装到系统,而不必要求与原作者打包的Linux环境相同。通过表格可以看出RPM与SRPM之间的差异,如表3-2所示。
表3-2 RPM与SRPM的比较
3.1.7 i386、i586、i686、noarch与x86 64
从3.1.6可知,RPM与SRPM的格式分别为:
xxxxxxxxx.rpm <==RPM的格式,已经经过编译且包装完成的rpm文件。 xxxxx.src.rpm <==SRPM的格式,包含未编译的源码信息。
通过文件名我们可以知道这个软件的版本、适用的平台、编译发布的次数。例如,rp-pppoe-3.1-5.i386.rpm的文件的意义为:
rp-pppoe- 3.1 - 5 .i386 .rpm 软件名称 软件的版本信息 发布的次数 适合的硬件平台 扩展名
除了后面适合的硬件平台与扩展名外,主要是以“-”来隔开各个部分,这样可以很方便地找到该软件的名称、版本信息、打包次数与操作的硬件平台。
1)软件名称
每一个软件都对应一个名称,上面的范例中“rp-pppoe”即为软件名称。
2)版本信息
每一次升级版本就需要有一个版本的信息,借以判断版本的新旧,通常版本又分为主版本和次版本。范例中主版本为3,在主版本的架构下更动部分源码内容而释出一个新的版本就是次版本,范例中次版本为1。
3)发布版本次数
通常就是编译的次数。那么为何需要重复地编译呢?这是由于同一版的软件中,可能由于有某些bug或者安全上的顾虑,所以必须要进行小幅度的更新(patch)或重设一些编译参数,配置完成之后重新编译并打包成RPM文件。
4)操作硬件平台
由于RPM可以适用在不同的操作平台上,但是不同平台配置的参数还是有所差异的。并且,我们可以针对比较高阶的CPU来进行最佳化参数的配置,这样才能够使用高阶CPU所带来的硬件加速功能,所以就存在i386、i586、i686、x86_64与noarch等不同的硬件平台,如表3-3所示。
表3-3 不同的硬件平台
受惠于目前x86系统的支持,新的CPU都能够运行旧型CPU所支持的软件,也就是说硬件方面都可以向下兼容,因此最低等级的i386软件可以安装在所有的x86硬件平台上面,不论是32位还是64位。但是反过来就不能安装。举例来说,目前硬件大都是64位的等级,因此可以在该硬件上面安装x86_64或i386等级的RPM软件,但在旧型主机上面,例如P-III/P-4 32位机器,就不能够安装x86_64的软件。
根据上面的说明,其实只要选择i386版本来安装在x86硬件上面就肯定没问题。但是如果强调性能的话,还是应该选择与硬件相匹配的RPM文件,因为安装的软件是针对CPU硬件平台进行参数最佳化的编译。
3.1.8 RPM属性依赖的解决方法:yum线上升级
为了重复利用既有的软件功能,很多软件都会以函数库的方式发布部分功能,以方便其他软件的调用,例如,PAM模块的验证功能。此外,为了节省用户的数据量,目前distributions在发布软件时,都会将软件的内容分为一般使用与开发使用(development)两大类,所以才会常常看到有类似pam-x.x.rpm与pam-devel-x.x.rpm的文件名。而默认情况下,大部分都不必安装software-devel-x.x.rpm,因为终端用户很少会去做开发软件的工作。
因此,RPM软件文件会有属性依赖的问题产生(其实所有的软件管理几乎都有这方面的情况存在)。由于RPM软件文件内部会记录依赖属性的数据,因此可以将这些依赖属性的软件列表,在需要安装软件的时候,先到这个列表中查找,同时与系统内已安装的软件相比较,没安装到的依赖软件进行同时安装,这就解决了依赖属性的问题。这种机制就是yum机制。
RHEL先将发布的软件存放到yum服务器内,然后分析这些软件的依赖属性问题,将软件内的记录信息写下来(header),再将这些信息分析后记录成软件相关性的清单列表,这些列表数据与软件所在的位置称为容器(repository)。当用户端有软件安装的需求时,用户端主机会主动向yum服务器的容器网址下载清单列表,然后通过清单列表的数据与本机RPM数据库相比较,就能够安装所有具有依赖属性的软件了。整个流程如图3-1所示。
图3-1 yum使用的流程示意图
当用户端有升级、安装的需求时,yum会向容器要求更新清单,使清单更新到本机的/var/cache/yum里面。当用户端实施更新、安装时,使用本机清单与本机的RPM数据库进行比较,这样就知道该下载什么软件了,接下来yum会到容器服务器(yum server)下载所需要的软件,然后再通过RPM的机制开始安装软件。