第22章 软件安装:源码与Tarball

我们在第1章Linux是什么当中提到了GNU计划与GPL授权所产生的自由软件与开放源码等。不过,前面的章节都还没有提到真正的开放源码是什么的信息。在这一章当中,我们将通过Linux操作系统里面的执行文件来理解什么是可执行的程序,以及了解什么是编译程序。另外,与程序息息相关的函数库(library)的信息也需要了解一番!不过,在这个章节当中,鸟哥并不是要你成为一个开放源码的程序设计师,而是希望你可以了解如何将开放源码的程序设计、加入函数库的原理、通过编译而成为可以执行的二进制程序,乃至最后该执行文件可被我们所使用的一连串过程!

了解上面的概念有什么好处呢?因为在Linux的世界里面,由于客制化的关系,有时候我们需要自行安装软件在自己的Linux系统上面,所以如果你有简单的程序编译概念,那么将很容易进行软件的安装。甚至在发生软件编译过程中的错误时,你也可以自行做一些简易的修改!而最传统的软件安装过程自然就是由源码编译而来的!所以,在这里我们将介绍最原始的软件管理方式:使用Tarball来安装与升级管理我们的软件。

22.1 开放源码的软件安装与升级简介

如果鸟哥想要在我的 Linux 服务器上面跑网页服务器(WWW server)这项服务,那么我应该要做些什么事呢?当然就一定需要安装网页服务器的软件。如果鸟哥的服务器上面没有这个软件的话,那当然也就无法启用WWW的服务。所以,想要在你的Linux上面进行一些有的没的功能,学会如何安装软件是很重要的一个课题!

咦!安装软件有什么难的?在Windows的操作系统上面安装软件时,不是只要一直按“下一步”就可以安装妥当了吗?话是这样说没错,不过,也由于如此,所以在Windows系统上面的软件都是一模一样的,也就是说,你无法修改该软件的源代码,因此,万一你想要增加或者减少该软件的某些功能时,大概只能求助于当初发行该软件的厂商了!(这就是所谓的商机吗?)

或许你会说:“唉呦!我不过是一般人,不会用到多余的功能,所以不太可能会更改到程序代码吧?”如果你这么想的话,很抱歉。是有问题的!怎么说呢?像目前网络上面的病毒、黑客软件、木马程序等,都可能对你的主机上面的某些软件造成影响,导致主机死机或者是其他数据损毁等的伤害。如果你可以通过安全信息单位所提供的修改方式进行修改,那么你将可以很快速地自行修补好该软件的漏洞,而不必一定要等到软件开发商提供修补的程序包。要知道,提早打补丁是很重要的一件事。

并不是软件开发商故意要搞出一个有问题的软件,而是某些程序代码当初设计时可能没有考虑周全,或者是程序代码与操作系统的权限设置并不相同所导致的一些漏洞。当然,也有可能是黑客通过某些攻击程序测试到程序的不周全所致。无论如何,只要有网络存在的一天,可以想象得到,程序的漏洞永远补不完!但能补多少就补多少吧!

这样说可以了解Linux的优点了吗?没错!因为Linux上面的软件几乎都是经过GPL的授权,所以每个软件几乎均提供源代码,并且你可以自行修改该程序代码,以符合你个人的需求呢!很棒吧?这就是开放源码的优点。不过,到底什么是开放源码?这些程序代码是什么?Linux 上面可以执行的相关软件与开放源码之间是如何转换的?不同版本的Linux之间能不能使用同一个可执行文件或者是该可执行文件需要由源代码的部分重新进行转换?这些都是需要理清概念的。下面我们先就源代码与可执行文件来进行说明。

22.1.1 什么是开放源码、编译程序与可执行文件

在讨论程序代码是什么之前,我们先来谈论一下什么是可执行文件。我们说过,在Linux系统上面,一个文件能不能被执行看的是有没有可执行的那个权限(具有x权限),不过,Linux系统上真正识别的可执行文件其实是二进制文件,例如/usr/bin/passwd,/bin/touch这些文件即为二进制程序代码。

或许你会说 shell script 不是也可以执行吗?其实 shell script 只是利用 shell(例如 bash)这个程序的功能进行一些判断式,而最终执行的除了 bash 提供的功能外,仍是调用一些已经编译好的二进制程序来执行的呢!当然,bash本身也是一个二进制程序。那么我怎么知道一个文件是否为二进制呢?还记得我们在第7章里面提到的file这个命令的功能吗?对!用它就是了!我们现在来测试一下!

先以系统的文件测试看看:

[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

如果是系统提供的 /etc/init.d/syslog 呢?

[root@www ~]# file /etc/init.d/syslog

/etc/init.d/syslog: Bourne-Again shell script text executable

如果是二进制程序而且是可以执行的时候,它就会显示可执行文件类(ELF 32-bit LSB executable),同时会说明是否使用共享库 (shared libs),而如果是一般的 script,那它就会显示出text executables 之类的字样!

事实上,syslog 的数据显示出 Bourne-Again … 那一行,是因为你的 scripts 上面第一行有声明 #!/bin/bash 的缘故,如果你将 script 的第一行去掉,那么不管/etc/init.d/syslog 的权限为何,它其实显示的是 ASCII 文本文件的信息!

既然 Linux 操作系统真正认识的其实是二进制程序,那么我们是如何做出这样的一个二进制的程序呢?首先,我们必须要写程序,用什么东西写程序?就是一般的文本处理器啊!鸟哥都喜欢使用vim来进行程序的编写,写完的程序就是所谓的源代码。这个程序代码文件其实就是一般的纯文本文件。在完成这个源码文件的编写之后,再来就是要将这个文件“编译”成为操作系统看得懂的二进制程序。而要编译自然就需要“编译程序”来操作,经过编译程序的编译与连接之后,就会生成一个可以执行的二进制程序。

举个例子来说,在Linux上面最标准的程序语言为C,所以我使用C的语法进行源代码的书写,写完之后,以Linux上标准的C语言编译程序gcc这个程序来编译,就可以制作一个可以执行的二进制程序。整个的流程如图22-1所示。

figure_0654_0382

图22-1 利用 gcc 编译程序进行程序的编译流程示意图

事实上,在编译的过程当中还会生成所谓的目标文件(Object file),这些文件是以.o 的扩展名形式存在的!至于 C 语言的源代码文件通常以.c 作为扩展名。此外,有的时候,我们会在程序当中引用、调用其他的外部子程序,或者是利用其他软件提供的“函数功能”,这个时候,我们就必须要在编译的过程当中将该函数库加进去,如此一来,编译程序就可以将所有的程序代码与函数库做一个链接(Link)以生成正确的执行文件。

总之,我们可以这么说:

开放源码:就是程序代码,写给人类看的程序语言,但机器并不认识,所以无法执行;

编译程序:将程序代码转译成为机器看得懂的语言,就类似翻译者的角色;

可执行文件:经过编译程序变成二进制程序后机器看得懂所以可以执行的文件。

22.1.2 什么是函数库

在前一小节的图 22-1 中,在编译的过程里面有提到函数库这东西。什么是函数库呢?先举个例子来说:我们的Linux系统上通常已经提供一个可以进行身份验证的模块,就是在第14章提到的PAM模块。这个 PAM 提供的功能可以让很多的程序在被执行的时候,除了可以验证用户登录的信息外,还可以将身份确认的数据记录在日志文件里面,以方便系统管理器的跟踪。

既然有这么好用的功能,那如果我要编写具有身份认证功能的程序时,直接引用该 PAM 的功能就好,如此一来,我就不需要重新设计认证机制。也就是说,只要在我写的程序代码里面,设置调用PAM的函数功能,我的程序就可以利用Linux原本就有的身份认证的程序了。除此之外,其实我们的Linux内核也提供了相当多的函数库来给硬件开发者利用。

函数库又分为动态与静态函数库,我们分别在后面的小节对它们加以说明。这里我们以一个简单的流程图来示意一个有调用外部函数库的程序的执行情况。而如果要在程序里面加入引用的函数库,就需要如图22-2所示,即在编译的过程当中就需要加入函数库的相关设置。事实上,Linux的内核提供很多的内核相关函数库与外部参数,这些内核功能在设计硬件的驱动程序的时候是相当有用的信息,这些内核相关信息大多放置在/usr/include,/lib, /usr/lib 里面。我们在本章的后续小节再来探讨。反正我们可以简单地这么想:

figure_0655_0383

图22-2 程序执行时引用外部动态函数库的示意图

函数库:类似子程序的角色,可以被调用来执行的一段功能函数。

22.1.3 什么是 make与configure

事实上,使用类似gcc的编译程序来进行编译的过程并不简单,因为一套软件并不会仅有一个程序,而是有一堆程序代码文件。所以除了每个主程序与子程序均需要写上一条编译过程的命令外,还需要写上最终的链接程序。程序代码小还好,如果是类似WWW服务器软件(例如Apache),或者是类似内核的源码,动则数百 MB 的数据量,编译命令会写到疯掉。这个时候,我们就可以使用 make这个命令的相关功能来进行编译过程的命令简化了。

当执行 make 时,make 会在当前的目录下搜索 Makefile(or makefile)这个文本文件,而 Makefile里面则记录了源码如何编译的详细信息。make会自动判别源码是否经过变动了而自动更新执行文件,是软件工程师相当好用的一个辅助工具呢!

make是一个程序,会去找Makefile,那Makefile怎么写?通常软件开发商都会写一个检测程序来检测用户的操作环境,以及该操作环境是否有软件开发商所需要的其他功能,该检测程序检测完毕后,就会主动新建这个Makefile的规则文件。通常这个检测程序的文件名为configure或者是config。

那为什么要检测操作环境呢?在第 1 章当中,不是曾经提过其实每个 Linux distribution 都使用同样的内核吗?但你得要注意,不同版本的内核所使用的系统调用可能不相同,而且每个软件所需要的相关的函数库也不相同,同时,软件开发商不会仅针对Linux开发,而是会针对整个UNIX-Like做开发啊!所以他也必须要检测该操作系统平台有没有提供合适的编译程序才行!所以当然要检测环境啊!一般来说,检测程序会检测的数据大约有下面这些:

是否有适合的编译程序可以编译本软件的程序代码;

是否已经存在本软件所需要的函数库或其他需要的相关软件;

操作系统平台是否适合本软件,包括Linux的内核版本;

内核的头定义文件(header include)是否存在(驱动程序必须要的检测)。

至于make与configure运行流程的相关性,我们可以使用图22-3来示意一下啊!下图中,你要进行的任务其实只有两个,一个是执行 configure 来新建 Makefile,这个步骤一定要成功!成功之后再以make来调用所需要的数据来编译即可,非常简单!

figure_0656_0384

图22-3 通过configure 与make 进行编译示意图

由于不同的 Linux distribution 的函数库文件所放置的路径,或者是函数库的文件名定义,或者是默认安装的编译程序,以及内核的版本都不相同,因此理论上,你无法在 CentOS 5.x 上面编译出二进制文件后,还将它拿到SuSE上面执行,这个操作通常是不可能成功的。因为调用的目标函数库位置可能不同(参考图22-2),内核版本更不可能相同,能够执行的情况是微乎其微。所以同一套软件要在不同的平台上面执行时,必须要重复编译,因此才需要源码。

详细的make用法与Makefile规则,在后续的小节里面再探讨!

22.1.4 什么是 Tarball 的软件

从前面几个小节的说明来看,我们知道所谓的源代码其实就是一些写满了程序代码的纯文本文件。那我们在第9章压缩命令的介绍当中,也了解了纯文本文件在网络上其实是很浪费带宽的一种文件格式。所以,如果能够将这些源码通过文件的打包与压缩技术来将文件的数量与容量减小,不但让用户容易下载,软件开发商的网站带宽也能够节省很多。这就是Tarball文件的由来。

想一想,一个内核的源码文件大约要 300~500MB 以上,如果每个人都去下载这样的一个内核文件,那么网络带宽会变得非常拥挤!

所谓的Tarball文件,其实就是将软件的所有源码文件先以tar打包,然后再以压缩技术来压缩,通常最常见的就是以gzip来压缩了。因为利用了tar与gzip的功能,所以tarball文件一般的扩展名就会写成.tar.gz或者是简写为.tgz。不过,近来由于bzip2的压缩率较佳,所以Tarball渐渐以bzip2的压缩技术来取代gzip,因此文件名也会变成*.tar.bz2之类的。所以说,Tarball是一个软件包,你将它解压缩之后,里面的文件通常就会有:

源代码文件;

检测程序文件(可能是configure 或config 等文件名);

本软件的简易说明与安装说明(INSTALL或README)。

其中最重要的是那个INSTALL或者是README这两个文件,通常你只要能够参考这两个文件, Tarball软件的安装是很简单的。我们在后面的章节会再继续介绍Tarball这个玩意。

22.1.5 如何安装与升级软件

将源码做了一个简单的介绍,也知道了系统其实认识的可执行文件是二进制文件之后,好了,得要聊一聊,那么怎么安装与升级一个Tarball的软件?为什么要安装一个新的软件呢?当然是因为我们的主机上面没有该软件。那么,为何要升级呢?原因可能有下面这些:

需要新的功能,但旧有主机的旧版软件并没有,所以需要升级到新版的软件;

旧版本的软件上面可能有安全上的顾虑,所以需要更新到新版的软件;

旧版的软件执行性能不佳,或者执行的能力不能让管理者满足。

在上面的需求当中,尤其需要注意的是第二点,当一个软件有安全上的顾虑时,千万不要怀疑,赶紧更新软件吧!否则造成网络危机,那可不是闹着玩的!那么更新的方法有哪些呢?基本上更新的方法可以分为两大类,分别是:

直接以源码通过编译来安装与升级;

直接以编译好的二进制程序来安装与升级。

上面第一点很简单,就是直接以Tarball在自己的机器上面进行检测、编译、安装与设置等操作来升级就是了。不过,这样的操作虽然让用户在安装过程当中具有很高的可选择性,但毕竟比较麻烦一点,如果 Linux distribution 厂商能够针对自己的操作平台先进行编译等过程,再将编译好的二进制程序释出的话,那由于我的系统与该 Linux distribution 的环境是相同的,所以它所释出的二进制程序就可以在我的机器上面直接安装,省略了检测与编译等等繁杂的过程呢!

这个预先编译好程序的机制存在于很多 distribution,包括 Red Hat 系统(含Fedora/CentOS 系列)开发的RPM软件管理机制与yum在线更新模式;Debian使用的dpkg软件管理机制与APT在线更新模式等。

由于 CentOS 系统是依循标准的 Linux distribution,所以可以使用 Tarball 直接进行编译的安装与升级,当然也可以使用RPM相关的机制来进行安装与升级。这里主要针对Tarball,至于RPM则留待下个章节再来介绍呢!

好了,那么一个软件的Tarball是如何安装的呢?基本流程是这样的:

1.将Tarball由厂商的网页下载下来;

2.将Tarball解压缩,生成很多的源码文件;

3.开始以gcc进行源码的编译(会生成目标文件);

4.然后以gcc进行函数库、主程序、子程序的链接,以形成主要的二进制文件;

5.将上述的二进制文件以及相关的配置文件安装至自己的主机上面。

上面第3、4步骤当中,我们可以通过make这个命令的功能来简化它,所以整个步骤其实是很简单的。只不过你就得需要至少有gcc以及make这两个软件在你的Linux系统里面才行。详细的过程以及需要的软件我们在后面的章节继续来介绍。

22.2 使用传统程序语言进行编译的简单范例

经过上面的介绍之后,你应该比较清楚地知道源码、编译程序、函数库与执行文件之间的相关性了。不过,详细的流程可能还是不很清楚,所以,在这里我们以一个简单的程序范例来说明整个编译的过程!赶紧进入Linux系统,实际操作一下下面的范例吧!

22.2.1 单一程序:打印 Hello World

我们以Linux上面最常见的C语言来编写第一个程序。第一个程序最常做的就是在屏幕上面打印“Hello World!”的字样。当然,这里我们是以简单的 C 语言来编写,如果你对于 C 有兴趣的话,那么请自行购买相关的书籍。好了,立刻编辑第一个程序吧!

请先确认你的 Linux 系统里面已经安装了 gcc 了!如果尚未安装 gcc 的话,请先参考下一节的 RPM 安装法,先安装好 gcc 之后,再回来阅读本章。如果你已经联网了,那么直接使用“ yum groupinstall "Development Tools" ” 预安装好所需的所有软件即可。rpm 与 yum 均会在下一章介绍。

编辑程序代码,即源码

[root@www ~]# vim hello.c <==用 C 语言写的程序扩展名建议用 .c

include <stdio.h>

int main(void)

{

printf("Hello World\n");

}

上面是用C语言的语法写成的一个程序文件。第一行的那个“#”并不是批注!如果你担心输入错误,请到下面的链接下载这个文件:

http://linux.vbird.org/linux_basic/0520source/hello.c

开始编译与测试执行

[root@www ~]# gcc hello.c

[root@www ~]# ll hello.c a.out

-rwxr-xr-x 1 root root 4725 Jun 5 02:41 a.out <==此时会产生这个文件名

-rw-r—r— 1 root root 72 Jun 5 02:40 hello.c

[root@www ~]# ./a.out

Hello World <==呵呵!结果出现了!

在默认的状态下,如果我们直接以 gcc 编译源码,并且没有加上任何参数,则执行文件的文件名会被自动设置为a.out这个文件名。所以你就能够直接执行./a.out这个执行文件。上面的例子很简单吧!那个 hello.c 就是源码,而 gcc 就是编译程序,至于 a.out 就是编译成功的可执行文件。如果我想要产生目标文件(object file)来进行其他的操作,而且执行文件的文件名也不要用默认的 a.out,那该如何是好?其实你可以将上面的第 2 个步骤改成这样:

[root@www ~]# gcc -c hello.c

[root@www ~]# ll hello*

-rw-r—r— 1 root root 72 Jun 5 02:40 hello.c

-rw-r—r— 1 root root 868 Jun 5 02:44 hello.o <==就是被生成的目标文件

[root@www ~]# gcc -o hello hello.o

[root@www ~]# ll hello*

-rwxr-xr-x 1 root root 4725 Jun 5 02:47 hello <==这就是可执行文件

-rw-r—r— 1 root root 72 Jun 5 02:40 hello.c

-rw-r—r— 1 root root 868 Jun 5 02:44 hello.o

[root@www ~]# ./hello

Hello World

这个步骤主要是利用 hello.o 这个目标文件制作出一个名为 hello 的执行文件,详细的 gcc语法我们会在后续章节中继续介绍。通过这个操作后,我们可以得到hello及hello.o两个文件,真正可以执行的是hello这个二进制文件。或许你会觉得只要一个操作得出a.out就好了,干嘛还要先制作目标文件再做成执行文件呢?通过下个范例,你就可以知道为什么。

22.2.2 主程序、子程序链接:子程序的编译

如果我们在一个主程序里面又调用了另一个子程序呢?这是很常见的一个程序写法,因为可以简化整个程序的易读性。在下面的例子当中,我们以 thanks.c 这个主程序去调用 thanks_2.c 这个子程序,写法很简单!

编写所需要的主程序、子程序

1. 编辑主程序:

[root@www ~]# vim thanks.c

include <stdio.h>

int main(void)

{

printf("Hello World\n");

thanks_2();

}

上面的“thanks_2();”那一行就是调用子程序!

[root@www ~]# vim thanks_2.c

include <stdio.h>

void thanks_2(void)

{

printf("Thank you!\n");

}

上面这两个文件你可以到下面下载:

http://linux.vbird.org/linux_basic/0520source/thanks.c

http://linux.vbird.org/linux_basic/0520source/thanks_2.c

进行程序的编译与链接(Link)

2. 开始将源码编译成为可执行的 binary file :

[root@www ~]# gcc -c thanks.c thanks_2.c

[root@www ~]# ll thanks*

-rw-r—r— 1 root root 76 Jun 5 16:13 thanks_2.c

-rw-r—r— 1 root root 856 Jun 5 16:13 thanks_2.o <==编译生成的

-rw-r—r— 1 root root 92 Jun 5 16:11 thanks.c

-rw-r—r— 1 root root 908 Jun 5 16:13 thanks.o <==编译生成的

[root@www ~]# gcc -o thanks thanks.o thanks_2.o

[root@www ~]# ll thanks*

-rwxr-xr-x 1 root root 4870 Jun 5 16:17 thanks <==最终会生成此结果

3. 执行一下这个文件:

[root@www ~]# ./thanks

Hello World

Thank you!

知道为什么要制作出目标文件了吗?由于我们的源码文件有时并非仅只有一个文件,所以我们无法直接进行编译。这个时候就需要先生成目标文件,然后再以链接制作成为二进制可执行文件。另外,如果有一天,你更新了 thanks_2.c 这个文件的内容,则你只要重新编译thanks_2.c 来产生新的 thanks_2.o,然后再以链接制作出新的二进制可执行文件即可,而不必重新编译其他没有改动过的源码文件。这对于软件开发者来说,是一个很重要的功能,因为有时候要将偌大的源码全部编译完成,会花很长的一段时间呢!

此外,如果你想要让程序在执行的时候具有比较好的性能或者是其他的调试功能时,可以在编译的过程里面加入适当的参数,例如下面的例子:

[root@www ~]# gcc -O -c thanks.c thanks_2.c <== -O 为生成优化的参数

[root@www ~]# gcc -Wall -c thanks.c thanks_2.c

thanks.c: In function 'main':

thanks.c:5: warning: implicit declaration of function 'thanks_2'

thanks.c:6: warning: control reaches end of non-void function

-Wall 为产生更详细的编译过程信息。上面的信息为警告信息 (warning)

所以不用理会也没有关系!

至于更多的 gcc 额外参数功能,就得要 man gcc,参数可多了,跟天书一样!

22.2.3 调用外部函数库:加入链接的函数库

刚才我们都仅只是在屏幕上面打印一些字眼而已,如果说要计算数学公式呢?例如我们想要计算出三角函数里面的sin(90°)。要注意的是,大多数的程序语言都是使用径度而不是一般我们在计算的“角度”,180°约等于3.14(π)。那我们就来写一下这个程序吧!

[root@www ~]# vim sin.c

include <stdio.h>

int main(void)

{

float value;

value = sin ( 3.14 / 2 );

printf("%f\n",value);

}

上面这个文件的内容可以在下面取得:

http://linux.vbird.org/linux_basic/0520source/sin.c

那要如何编译这个程序呢?我们先直接编译看看:

[root@www ~]# gcc sin.c

sin.c: In function 'main':

sin.c:5: warning: incompatible implicit declaration of built-in function 'sin'

/tmp/ccsfvijY.o: In function `main':

sin.c:(.text+0x1b): undefined reference to `sin'

collect2: ld returned 1 exit status

注意看到上面最后一行,会有个错误信息,代表没有成功!

特别注意上面的错误信息,怎么没有编译成功?它说的是“undefined reference to sin”,说的是“没有sin的相关定义参考值”,为什么会这样呢?这是因为C语言里面的sin函数是写在libm.so这个函数库中,而我们并没有在源码里面将这个函数库功能加进去,所以当然就需要在编译与链接的时候将这个函数库链接进执行文件里面啊!我们可以这样做:

编译时加入额外函数库链接的方式:

[root@www ~]# gcc sin.c -lm -L/lib -L/usr/lib <==重点在 -lm

[root@www ~]# ./a.out      <==尝试执行新文件

1.00 0000

特别注意,使用gcc编译时所加入的那个-lm是有意义的,它可以拆开成两部分来看:

-l:是加入某个函数库(library)的意思;

m:则是libm.so这个函数库,其中,lib与扩展名(.a或.so)不需要写。

所以-lm表示使用libm.so(或libm.a)这个函数库的意思。至于那个-L后面接的路径呢?这表示我要的函数库libm.so请到/lib或/usr/lib里面搜索。

上面的说明很清楚了吧?不过,要注意的是,由于Linux默认是将函数库放置在/lib与/usr/lib当中,所以你没有写-L/lib 与-L/usr/lib 也没有关系的!不过,万一哪天你使用的函数库并非放置在这两个目录下,那么-L/path就很重要了,否则会找不到函数库!

除了链接的函数库之外,你或许已经发现一个奇怪的地方,那就是在我们的sin.c当中第一行“#include<stdio.h>“,这行说的是要将一些定义数据由stdio.h这个文件读入,这包括printf的相关设置。这个文件其实是放置在/usr/include/stdio.h的。那么万一这个文件并非放置在这里呢?那么我们就可以使用下面的方式来定义出要读取的include文件放置的目录:

[root@www~]# gcc sin.c -lm -I/usr/include

-I/path 后面接的路径(Path)就是设置要去搜索相关的 include 文件的目录。不过,同样,默认值是放置在/usr/include 下面,除非你的 include 文件放置在其他路径,否则也可以略过这个选项。

通过上面的几个小范例,你应该对于 gcc 以及源码有一定程度的认识了,再接下来,我们来稍微整理一下gcc的简易使用方法吧!

22.2.4 gcc 的简易用法(编译、参数与链接)

前面说过,gcc为Linux上面最标准的编译程序,这个gcc是由GNU计划所维护的,有兴趣的朋友请自行前往参考。既然gcc对于Linux上的开放源码是这么重要,所以下面我们就列举几个gcc常见的参数,如此一来大家应该更容易了解源码的各项功能吧!

仅将源码编译成为目标文件,并不制作链接等功能:

[root@www ~]# gcc -c hello.c

会自动生成 hello.o 这个文件,但是并不会生成可执行文件。

在编译的时候,依据操作环境给予优化执行速度

[root@www ~]# gcc -O hello.c -c

会自动的生成 hello.o 这个文件,并且进行优化!

在进行二进制文件制作时,将链接的函数库与相关的路径填入

[root@www ~]# gcc sin.c -lm -L/usr/lib -I/usr/include

这个命令较常执行在最终链接成 binary file 的时候,

-lm 指的是 libm.so 或 libm.a 这个函数库文件;

-L 后面接的路径是刚才上面那个函数库的搜索目录;

-I 后面接的是源码内的 include 文件的所在目录。

将编译的结果输出成某个特定文件名

[root@www ~]# gcc -o hello hello.c

-o 后面接的是要输出的 binary file 文件名

在编译的时候,输出较多的信息说明

[root@www ~]# gcc -o hello hello.c -Wall

加入 -Wall 之后,程序的编译会变得较为严谨一点,

所以警告信息也会显示出来!

比较重要的大概就是这一些。另外,我们通常称-Wall或者-O这些非必要的参数为标志(FLAGS),因为我们使用的是C程序语言,所以有时候也会简称这些标识为CFLAGS,这些变量偶尔会被使用,尤其是在后头会介绍的make相关的用法时更是重要!

22.3 用make 进行宏编译

在本章一开始我们提到过make的功能是可以简化编译过程里面所执行的命令,同时还具有很多很方便的功能。那么下面咱们就来试看看使用make简化执行编译命令的流程吧!

22.3.1 为什么要用make

先来想象一个案例,假设我的执行文件里面包含了 4 个源码文件,分别是 main.c、haha. c、sin_value.c 和 cos_value.c 这 4 个文件,这 4 个文件的目的是:

main.c:让用户输入角度数据与调用其他3个子程序;

haha.c:输出一堆信息而已;

sin_value.c:计算用户输入的角度(360)的sin数值;

cos_value.c:计算用户输入的角度(360)的cos数值。

这4个文件你可以到http://linux.vbird.org/linux_basic/0520source/main.tgz下载。由于这4个文件里面包含了相关性,并且还用到数学函数在里面,所以如果你想要让这个程序可以运行,那么就需要这样编译:

1. 先进行目标文件的编译,最终会有四个 *.o 的文件名出现:

[root@www ~]# gcc -c main.c

[root@www ~]# gcc -c haha.c

[root@www ~]# gcc -c sin_value.c

[root@www ~]# gcc -c cos_value.c

2. 再进行链接成为可执行文件,并加入 libm 的数学函数,以生成 main 可执行文件:

[root@www ~]# gcc -o main main.o haha.o sin_value.o cos_value.o \

> -lm -L/usr/lib -L/lib

3. 本程序的执行结果必须输入姓名、360 度角的角度值来计算:

[root@www ~]# ./main

Please input your name: VBird <==这里先输入名字

Please enter the degree angle (ex> 90): 30 <==输入以 360 度角为主的角度

Hi, Dear VBird, nice to meet you. <==这三行为输出的结果

The Sin is: 0.50

The Cos is: 0.87

编译的过程需要进行好多操作,而且如果要重新编译,则上述的流程得要重新来一遍,光是找出这些命令就够烦人的了!如果可以的话,能不能一个步骤就完成上面所有的操作呢?那就利用 make这个工具吧!先试看看在这个目录下新建一个名为makefile的文件,内容如下:

1. 先编辑 makefile 这个规则文件,内容只要制作出 main 这个可执行文件

[root@www ~]# vim makefile

main: main.o haha.o sin_value.o cos_value.o

gcc -o main main.o haha.o sin_value.o cos_value.o -lm

注意:第二行的 gcc 之前是 <tab> 按键生成的空格!

2. 尝试使用 makefile 制订的规则进行编译的行为:

[root@www ~]# rm -f main *.o <==先将之前的目标文件去除

[root@www ~]# make

cc -c -o main.o main.c

cc -c -o haha.o haha.c

cc -c -o sin_value.o sin_value.c

cc -c -o cos_value.o cos_value.c

gcc -o main main.o haha.o sin_value.o cos_value.o -lm

此时 make 会去读取 makefile 的内容,并根据内容直接去编译相关的文件!

3. 在不删除任何文件的情况下,重新执行一次编译的操作:

[root@www ~]# make

make: `main' is up to date.

看到了吧!是否很方便呢?只会进行更新 (update) 的操作而已。

或许你会说:“如果我建立一个 shell script 来将上面的所有操作都集合在一起,不是具有同样的效果吗?”呵呵!效果当然不一样,以上面的测试为例,我们仅写出main需要的目标文件,结果make会主动去判断每个目标文件相关的源码文件,并直接予以编译,最后再直接进行链接的操作。真的是很方便啊!此外,如果我们改动过某些源码文件,则make也可以主动判断哪一个源码与相关的目标文件文件有更新过,并仅更新该文件,如此一来,将可大大节省很多编译的时间。要知道,某些程序在进行编译的行为时,会消耗很多的CPU资源。所以说,make有这些好处:

简化编译时所需要执行的命令;

若在编译完成之后,修改了某个源码文件,则make仅会针对被修改了的文件进行编译,其他的目标文件不会被更改;

最后可以依照相依性来更新(update)执行文件。

既然make 命令有这么多的优点,那么我们当然就得好好了解一下 make!而make 里面最需要注意的大概就是那个规则文件,也就是makefile这个文件的语法!所以下面我们就针对makefile的语法来加以介绍。

22.3.2 makefile 的基本语法与变量

make的语法可是相当多而复杂的,有兴趣的话可以到GNU [10]去查阅相关的说明,鸟哥这里仅列出一些基本的规则,重点在于让读者们将来在接触源码时不会太紧张啊!好了,基本的makefile规则是这样的:

目标(target): 目标文件1 目标文件2

<tab> gcc -o 欲新建的可执行文件 目标文件1 目标文件2

那个目标(target)就是我们想要建立的信息,而目标文件就是具有相关性的 object files,那建立可执行文件的语法就是以<tab>按键开头的那一行!特别留意的是:命令行必须要以 tab 按键作为开头才行!它的规则基本上是这样的:

在makefile当中的#代表批注;

<tab>需要在命令行(例如gcc这个编译程序命令)的第一个字符;

目标(target)与相关文件(就是目标文件)之间需以“:”隔开。

同样,我们以刚才上一个小节的范例进一步说明,如果我想要有两个以上的执行操作时,例如执行一个命令就直接清除掉所有的目标文件与可执行文件,该如何制作呢?

1. 先编辑 makefile 来建立新的规则,此规则的目标名称为 clean :

[root@www ~]# vi makefile

main: main.o haha.o sin_value.o cos_value.o

gcc -o main main.o haha.o sin_value.o cos_value.o -lm

clean:

rm -f main main.o haha.o sin_value.o cos_value.o

2. 以新的目标 (clean) 测试看看执行 make 的结果:

[root@www ~]# make clean <==就是这里!通过 make 以 clean 为目标

rm -rf main main.o haha.o sin_value.o cos_value.o

如此一来,我们的 makefile 里面就具有至少两个目标,分别是 main 与clean,如果我们想要建立 main 的话,输入“make main”,如果想要清除信息,输入“make clean”即可。而如果想要先清除目标文件再编译 main 这个程序的话,就可以这样输入:makeclean main,如下所示:

[root@www ~]# make clean main

rm -rf main main.o haha.o sin_value.o cos_value.o

cc -c -o main.o main.c

cc -c -o haha.o haha.c

cc -c -o sin_value.o sin_value.c

cc -c -o cos_value.o cos_value.c

gcc -o main main.o haha.o sin_value.o cos_value.o -lm

这样就很清楚了吧!但是,你是否会觉得makefile里面怎么重复的数据这么多啊?没错!所以我们可以再通过 shell script 那时学到的“变量”来更简化 makefile:

[root@www ~]# vi makefile

LIBS = -lm

OBJS = main.o haha.o sin_value.o cos_value.o

main: ${OBJS}

gcc -o main ${OBJS} ${LIBS}

clean:

rm -f main ${OBJS}

与 bash shell script 的语法有点不太相同,变量的基本语法为:

1.变量与变量内容以“=”隔开,同时两边可以具有空格;

2.变量左边不可以有<tab>,例如上面范例的第一行LIBS左边不可以是<tab>;

3.变量与变量内容在“=”两边不能具有“:”;

4.在习惯上,变量最好是以“大写字母”为主;

5.运用变量时,以${变量}或$(变量)使用;

6.在该shell的环境变量是可以被套用的,例如提到的CFLAGS这个变量;

7.在命令行模式也可以定义变量。

由于gcc在进行编译的行为时,会主动去读取CFLAGS这个环境变量,所以,你可以直接在shell定义出这个环境变量,也可以在makefile文件里面去定义,更可以在命令列当中定义。例如:

[root@www ~]# CFLAGS="-Wall" make clean main

这个操作在上 make 进行编译时,会去取用 CFLAGS 的变量内容!

也可以这样:

[root@www ~]# vi makefile

LIBS = -lm

OBJS = main.o haha.o sin_value.o cos_value.o

CFLAGS = -Wall

main: ${OBJS}

gcc -o main ${OBJS} ${LIBS}

clean:

rm -f main ${OBJS}

我可以利用命令行进行环境变量的输入,也可以在文件内直接指定环境变量,那万一这个CFLAGS的内容在命令行与makefile里面并不相同时,以哪个方式输入的为主?问了个好问题啊!环境变量取用的规则是这样的:

1.make命令行后面加上的环境变量为第一优先;

2.makefile里面指定的环境变量第二优先;

3.shell原本具有的环境变量第三优先。

此外,还有一些特殊的变量需要了解的:

$@:代表目前的目标(target)

所以我也可以将makefile改成:

[root@www~]# vi makefile

LIBS = -lm

OBJS = main.o haha.o sin_value.o cos_value.o

CFLAGS = -Wall

main: ${OBJS}

gcc -o $@ ${OBJS} ${LIBS} <==那个 $@ 就是 main !

clean:

rm-fmain${OBJS}

这样是否稍微了解了makefile(也可能是Makefile)的基本语法?这对于你将来自行修改源码的编译规则时是很有帮助的。

22.4 Tarball 的管理与建议

在我们知道了源码的相关信息之后,再来要了解的自然就是如何使用具有源码的Tarball来建立一个属于自己的软件!从前面几个小节的说明当中,我们晓得其实Tarball的安装是可以跨平台的,因为C语言的程序代码在各个平台上面是可以共通的,只是需要的编译程序可能并不相同而已。例如Linux上面用 gcc而 Windows上面也有相关的 C编译程序。所以,同样的一组源码既可以在 CentOS Linux上面编译,也可以在 SuSE Linux 上面编译,当然,也可以在大部分的 UNIX 平台上面编译成功的!

如果万一没有编译成功怎么办?很简单啊,通过修改小部分的程序代码(通常是因为很小部分的变动而已)就可以进行跨平台的移植了!也就是说,刚才我们在Linux下面写的程序理论上是可以在Windows上面编译的!这就是源码的好处!所以说,如果朋友们想要学习程序语言的话,鸟哥个人是比较建议学习具有跨平台能力的程序语言,例如C就是很不错的一个!

哎呀!又扯远了!赶紧回来继续说明我们的Tarball!

22.4.1 使用源码管理软件所需要的基础软件

从源码的说明我们晓得要制作一个二进制文件需要很多内容的呢!这包括下面这些基础的软件:

gcc或cc等C语言编译程序(compiler)

没有编译程序怎么进行编译的操作?所以 C 编译器是一定要有的。不过 Linux 上面有众多的编译程序,其中当然以GNU的gcc是首选的自由软件编译程序!事实上很多在Linux平台上面发展的软件的源码原本就是以gcc为底来设计的呢!

make及autoconfig等软件

一般来说,以 Tarball 方式释出的软件当中,为了简化编译的流程,通常都是配合前几个小节提到的make这个命令来依据目标文件的相依性而进行编译。但是我们也知道make需要makefile这个文件的规则,那由于不同的系统里面可能具有的基础软件环境并不相同,所以就需要检测用户的操作环境,好自行建立一个makefile文件。这个自行检测的小程序也必须要通过autoconfig这个相关的软件来辅助才行。

需要Kernel提供的Library以及相关的Include文件

从前面的源码编译过程,我们知道函数库(library)的重要性,同时也晓得有 include 文件的存在。很多的软件在发展的时候都是直接取用系统内核提供的函数库与include文件的,这样才可以与这个操作系统兼容啊!尤其是在驱动程序方面的模块,例如网卡、声卡、USB等驱动程序在安装的时候经常是需要内核提供的相关信息的。在Red Hat的系统当中(包含Fedora/CentOS等系列),这个内核相关的功能通常都是被包含在kernel-source或kernel-header这些软件当中,所以记得要安装这些软件!

虽然Tarball的安装上面相当简单,如同我们前面几个小节的例子,只要顺着开发商提供的README与INSTALL文件所载明的步骤来进行,安装是很容易的。但是我们却还是经常会在BBS或者是新闻组当中发现这些留言:“我在执行某个程序的检测文件时,它都会告诉我没有gcc这个软件,这是怎么回事?”还有:“我没有办法使用make耶!这是什么问题?”这就是没有安装上面提到的那些基础软件!

为什么用户不安装这些软件呢?这是因为目前的Linux distribution 大多已经偏向于桌面计算机的使用(非服务器端),他们希望用户能够按照厂商自己的希望来安装相关的软件即可,所以通常“默认”是没有安装gcc或者是make等软件的。所以,如果你希望将来可以自行安装一些以Tarball方式释出的软件时,记得请自行挑选想要安装的软件名称!例如在 CentOS 或者是 Red Hat 当中记得选择Development Tools 以及 Kernel Source Development 等相关字眼的软件呢。

那万一我已经安装好一台 Linux 主机,但是使用的是默认值所安装的软件,所以没有 make、gcc等,该如何是好?问题其实不大,目前使用最广泛的CentOS/Fedora或者是Red Hat大多是以RPM (下一章会介绍)来安装软件的,所以,你只要拿出当初安装Linux时的原版光盘,然后以下一章介绍的RPM来一个一个加入到你的Linux主机里面就好!很简单的!尤其现在又有yum这玩意儿,更方便!

在CentOS当中,如果你已经可以连上Internet的话,那么就可以使用下一章会谈到的yum。通过yum的软件组安装功能,你可以这样做:

如果是要安装 gcc 等软件开发工具,请使用“yum groupinstall "Development Tools" ”;

若待安装的软件需要图形接口支持,一般还需要“yum groupinstall "X Software Development"”;

若安装的软件较旧,可能需要“yum groupinstall "Legacy Software Development"”。

大概就是这样,更多的信息请参考下一章的介绍。

22.4.2 Tarball 安装的基本步骤

我们提过以Tarball方式释出的软件是需要重新编译可执行的二进制文件的。而Tarball是以tar这个命令来打包与压缩的文件,所以,当然就需要先将 Tarball 解压缩,然后到源码所在的目录下进行makefile的建立,再以make来进行编译与安装的操作啊!所以整个安装的基础操作大多是这样的:

1.取得源文件:将tarball文件在/usr/local/src目录下解压缩;

2.取得步骤流程:进入新建立的目录下面,去查阅INSTALL与README等相关文件内容(很重要的步骤);

3.相关属性软件安装:根据INSTALL/README 的内容查看并安装好一些相关的软件(非必要);

4.建立makefile:以自动检测程序(configure或config)检测操作环境,并建立Makefile这个文件;

5.编译:以 make 这个程序并使用该目录下的 Makefile 作为它的参数配置文件,来进行 make(编译或其他)的操作;

6.安装:以make这个程序,并以Makefile这个参数配置文件,依据install这个目标(target)的指定来安装到正确的路径。

注意到上面的第二个步骤,通常在每个软件在释出的时候,都会附上INSTALL或者是README这种文件名的帮助文件,这些帮助文件请详细阅读过一遍,通常这些文件会记录这个软件的安装要求、软件的工作项目与软件的安装参数设置及技巧等,只要仔细读完这些文件,基本上,要安装好tarball的文件,都不会有什么大问题。

至于makefile在制作出来之后,里头会有相当多的目标(target),最常见的就是install与clean。通常“makeclean”代表着将目标文件(object file)清除掉,“make”则是将源代码进行编译而已。注意,编译完成的可执行文件与相关的配置文件还在源代码所在的目录当中!因此,最后要进行“makeinstall”来将编译完成的所有内容都安装到正确的路径去,这样就可以使用该软件!

我们下面简略提一下大部分的tarball软件之安装的命令执行方式:

1../configure

这个步骤就是建立Makefile这个文件。通常程序开发者会写一个script来检查你的Linux系统、相关的软件属性等,这个步骤相当重要,因为将来你的安装信息都是这一步骤内完成的。另外,这个步骤的相关信息应该要参考一下该目录下的README或INSTALL相关的文件。

2.makeclean

make会读取Makefile中关于clean的工作。这个步骤不一定会有,但是希望执行一下,因为它可以去除目标文件。因为谁也不确定源码里面到底有没有包含上次编译过的目标文件(*.o)存在,所以当然还是清除一下比较妥当的。至少等一下新编译出来的执行文件我们可以确定是使用自己的机器所编译完成的。

3.make

make会依据Makefile当中的默认工作进行编译的行为。编译的工作主要是进行gcc来将源码编译成为可以被执行的目标文件,但是这些目标文件通常还需要一些函数库之类的链接后,才能生成一个完整的可执行文件!使用 make 就是要将源码编译成为可以被执行的可执行文件,而这个可执行文件会放置在目前所在的目录之下,尚未被安装到预定安装的目录中。

4.make install

通常这就是最后的安装步骤了,make会依据Makefile这个文件里面关于install的选项,将上一个步骤所编译完成的数据安装到默认的目录中,就完成安装!

请注意,上面的步骤是一步一步来进行的,而其中只要一个步骤无法成功,那么后续的步骤就完全没有办法进行的!因此,要确定每一的步骤都是成功的才可以。举个例子来说,万一今天你在./configure 就不成功了,那么就表示 Makefile 无法被建立起来,要知道,后面的步骤都是根据Makefile来进行的,既然无法建立Makefile,后续的步骤当然无法成功!

另外,如果在 make 无法成功的话,那就表示源文件无法被编译成可执行文件,那么 make install主要是将编译完成的文件放置到文件系统中的,既然都没有可用的执行文件了,怎么进行安装?所以,要每一个步骤都正确无误才能往下继续做!此外,如果安装成功,并且是安装在独立的一个目录中,例如/usr/local/packages 这个目录中好了,那么你就必须手动将这个软件的 man page 写入/etc/man.config里面去。

22.4.3 一般Tarball 软件安装的建议事项(如何删除、升级)

或许你已经发现了也说不定,那就是为什么前一个小节里面,Tarball要在/usr/local/src里面解压缩呢?基本上,在默认的情况下,原本的 Linux distribution 发布安装的软件大多是在/usr 里面的,而用户自行安装的软件则建议放置在/usr/local里面,这是考虑到管理用户所安装软件的便利性。

怎么说呢?我们知道几乎每个软件都会提供在线帮助的服务,那就是info与man的功能。在默认的情况下,man会去搜索/usr/local/man里面的说明文件,因此,如果我们将软件安装在/usr/local下面的话,那么自然安装完成之后,该软件的说明文件就可以被找到了。此外,如果你所管理的主机其实是由多人共同管理的,或者是如同学校里面,一部主机是由学生管理的,但是学生总会毕业吧?所以需要进行交接,如果大家都将软件安装在/usr/local下面,那么管理上不就显得特别容易吗!

所以,通常我们会建议大家将自己安装的软件放置在/usr/local下,至于源码(Tarball)则建议放置在/usr/local/src(src为source的缩写)下面。

再来,让我们先来看一看 Linux distribution 默认的安装软件的路径会用到哪些?我们以 apache这个软件来说明的话(apache是WWW服务器软件,详细的数据请参考服务器架设篇。你的系统不见得有装这个软件):

/etc/httpd

/usr/lib

/usr/bin

/usr/share/man

我们会发现软件的内容大致上是摆在etc,lib,bin,man等目录当中,分别代表配置文件、函数库、可执行文件、在线帮助文档。好了,如果你是以 tarball 来安装时呢?如果是放在默认的/usr/local 里面,由于/usr/local原本就默认这几个目录了,所以你的数据就会被放在:

/usr/local/etc

/usr/local/bin

/usr/local/lib

/usr/local/man

但是如果你每个软件都选择在这个默认的路径下安装的话,那么所有的软件的文件都将放置在这4 个目录当中,因此,如果你都安装在这个目录下的话,那么将来再想要升级或删除的时候,就会比较难以追查文件源。而如果你在安装的时候选择的是单独的目录,例如我将 apache 安装在/usr/local/apache当中,那么你的文件目录就会变成:

/usr/local/apache/etc

/usr/local/apache/bin

/usr/local/apache/lib

/usr/local/apache/man

单一软件的文件都在同一个目录之下,那么要移除该软件就简单得多了!只要将该目录移除即可视为该软件已经被删除。以上面为例,我想要删除 apache 只要执行“rm -rf/usr/local/apache”就算删除这个软件。当然,实际安装的时候还是得视该软件的Makefile里头的install信息才能知道到底它的安装情况为何的,因为例如sendmail的安装就很麻烦。

这个方式虽然有利于软件的删除,但不知道你有没有发现,我们在执行某些命令的时候与该命令是否在 PATH 这个环境变量所记录的路径有关,以上面为例,我的/usr/local/apache/bin 肯定是不在 PATH 里面的,所以执行 apache 的命令就得要利用绝对路径了,否则就得将这个/usr/local/apache/bin 加入 PATH 里面。另外,那个/usr/local/apache/ man 也需要加入 man page搜索的路径当中啊!

除此之外,Tarball 在升级的时候也是挺困扰的,怎么说呢?我们还是以 apache 来说明好了。WWW服务器为了考虑互动性,所以通常会将PHP+MySQL+Apache一起安装起来(详细的信息请参考服务器架设篇),果真如此的话,那么每个软件在安装的时候都有一定的顺序与程序!因为它们三者之间具有相关性,所以安装时需要三者同时考虑到它们的函数库与相关的编译参数。

假设今天我只要升级 PHP 呢?有的时候因为只有涉及动态函数库的升级,那么我只要升级 PHP即可!其他的部分或许影响不大。但是如果今天PHP需要重新编译的模块比较多,那么可能会连带地连Apache这个程序也需要重新编译过才行,真是有点头痛!没办法!使用tarball确实有它的优点,但是在这方面确实也有它一定的伤脑筋程度。

由于Tarball在升级与安装上面具有这些特色,亦即Tarball在反安装上面具有比较高的难度(如果你没有好好规划的话),所以,为了方便Tarball的管理,通常鸟哥会这样建议用户:

1.最好将tarball的原始数据解压缩到/usr/local/src当中;

2.安装时,最好安装到/usr/local这个默认路径下;

3.考虑将来的反安装步骤,最好可以将每个软件单独安装在/usr/local下面;

4.为安装到单独目录的软件的man page 加入man path 搜索。

如果你安装的软件放置到/usr/local/software/中,那么在 man page 搜索的设置中可能就得要在/etc/man.config内的40~50行左右处写入如下的一行:

MANPATH/usr/local/software/man

这样才可以使用man来查询该软件的在线文件!

22.4.4 一个简单的范例(利用ntp 来示范)

读万卷书不如行万里路啊!所以当然我们就来测试一下,看你是否真的了解了如何利用 Tarball来安装软件呢?我们利用时间服务器(network time protocol,ntp)这个软件来测试安装看看。先请到http://www.ntp.org/downloads.html这个目录去下载文件,请下载最新版本的文件即可,或者直接到鸟哥的网站下载2009/05公告发布的稳定版本:

http://linux.vbird.org/linux_basic/0520source/ntp-4.2.4p7.tar.gz

假设我对这个软件的要求是这样的:

假设ntp-4.2.4p7.tar.gz这个文件放置在/root这个目录下;

源码请解压缩在/usr/local/src下面;

我要安装到/usr/local/ntp这个目录中。

那么你可以依照下面的步骤来安装测试看看。(如果可以的话,请你不要参考下面的文件数据,先自行安装过一遍这个软件,然后再来对照一下鸟哥的步骤。)

解压缩下载的tarball,并参阅README/INSTALL文件

[root@www ~]# cd /usr/local/src <==切换目录

[root@www src]# tar -zxvf /root/ntp-4.2.4p7.tar.gz <==解压缩到此目录

ntp-4.2.4p7/  <==会新建这个目录

ntp-4.2.4p7/libopts/

….(下面省略)….

[root@www src]# cd ntp-4.2.4p7/

[root@www ntp-4.2.4p7]# vi INSTALL <==记得 README 也要看一下

特别看一下 28 行到 54 行之间的安装简介!可以了解安装的流程!

检查configure支持参数,并实际生成makefile规则文件

[root@www ntp*]# ./configure —help | more <==查询可用的参数有哪些

—prefix=PREFIX  install architecture-independent files in PREFIX

—enable-all-clocks + include all suitable non-PARSE clocks:

—enable-parse-clocks - include all suitable PARSE clocks:

上面列出的是比较重要的,或者是你可能需要的参数功能!

[root@www ntp*]# ./configure —prefix=/usr/local/ntp \

> —enable-all-clocks —enable-parse-clocks <==开始建立makefile

checking for a BSD-compatible install… /usr/bin/install -c

checking whether build environment is sane… yes

….(中间省略)….

checking for gcc… gcc  <==也有找到 gcc 编译程序了

….(中间省略)….

config.status: creating Makefile <==现在知道这个重要性了吧

config.status: creating config.h

config.status: executing depfiles commands

一般来说 configure 较重要的就是那个—prefix=/path 了,—prefix 后面接的路径表示这个软件将来要安装到哪个目录去,如果你没有指定—prefix=/path这个参数,通常默认参数就是/usr/local。至于其他的参数意义就得要参考./configure—help 了。这个操作完成之后会产生makefile或Makefile这个文件。当然,这个检测检查的过程会显示在屏幕上,特别留意关于gcc的检查,还有最重要的是最后需要成功的新建起Makefile才行!

最后开始编译与安装!

[root@www ntp*]# make clean; make

[root@www ntp*]# make check

[root@www ntp*]# make install

将数据安装在 /usr/local/ntp 下面

整个操作就这么简单,你完成了吗?完成之后到 /usr/local/ntp 下你发现了什么?

22.4.5 利用patch更新源码

我们在本章一开始介绍了为何需要进行软件的升级,这是很重要的。那假如我是以 Tarball 来进行某个软件的安装,那么是否当我要升级这个软件时,就得要下载这个软件的完整全新的 Tarball 呢?举个例子来说,鸟哥帮别人在http://www.dic.ksu.edu.tw/phpbb3 这个网址架设了个论坛,这个论坛是以phpBB这个软件来架设的,而鸟哥的论坛版本为phpbb3.0.4.tar.gz,目前(2009/06)最新发布的版本则是phpbb3.0.5.tar.gz。那我是否需要下载全新的phpbb3.0.5.tar.gz这个文件来更新原本的旧程序呢?

事实上,当我们发现一些软件的漏洞,通常是某一段程序代码写得不好所致。因此,所谓的“更新源码”经常是只有更改部分文件的小部分内容而已。既然如此的话,那么我们是否可以就那些被改动的文件来进行修改就可以了?也就是说,旧版本到新版本间没有改动过的文件就不要理它,仅将有修改过的文件部分来处理即可。

这有什么好处呢?首先,没有改动过的文件的目标文件根本就不需要重新编译,而且改动过的文件又可以利用 make 来自动 update(更新),如此一来,我们原先的设置(makefile 文件里面的规则) 将不需要重新改写或检测,可以节省很多宝贵的时间呢(例如后续章节会提到的内核的编译)。

从上面的说明当中,我们可以发现,如果可以将旧版的源码数据改写成新版的版本,那么就能直接编译了,而不需要将全部的新版Tarball重新下载一次,可以节省带宽与时间。那么如何改写源码?难道要我们一个文件一个文件去参考然后修改吗?当然没有这么没人性!

我们在第12章介绍正则表达式的时候有提到一个比较文件的命令,那就是diff,这个命令可以将两个文件之间的差异性列出来呢!那我们也知道新旧版本的文件之间其实只有修改一些程序代码而已,那么我们可以通过diff比较出新旧版本之间的文字差异,然后再以相关的命令来将旧版的文件更新吗?当然可以!那就是patch这个命令!很多的软件开发商在更新了源码之后,几乎都会发布所谓的patch file,也就是直接将源码更新而已的一个方式!我们下面以一个简单的范例来说明。

关于diff与patch的基本用法我们在第12章都谈过了,所以这里不再就这两个命令的语法进行介绍,请回去参阅第 12 章的内容。这里我们来举个案例解释一下好了。假设我们刚才计算三角函数的程序(main)历经多次改版,0.1版仅会简单输出,0.2版的输出就会含有角度值,因此这两个版本的内容不相同。如下所示,两个文件的意义为:

http://linux.vbird.org/linux_basic/0520source/main-0.1.tgz:main的0.1版;

http://linux.vbird.org/linux_basic/0520source/main_0.1_to_0.2.patch:main由0.1升级到0.2 的 patch file。

请你先下载这两个文件,并且解压缩到你的/root下面,你会发现系统生成一个名为main-0.1的目录。该目录内含有五个文件,就是刚才的程序加上一个Makefile的规则文件。你可以到该目录下去看看Makefile的内容,在这一版当中含有main与clean两个目标功能而已。至于0.2版则加入了install与uninstall的规则设置。接下来,请看一下我们的做法:

测试旧版程序的功能

[root@www ~]# tar -zxvf main-0.1.tgz

[root@www ~]# cd main-0.1

[root@www main-0.1]# make clean main

[root@www main-0.1]# ./main

version 0.1

Please input your name: VBird

Please enter the degree angle (ex> 90): 45

Hi, Dear VBird, nice to meet you.

The Sin is: 0.71

The Cos is: 0.71

与之前的结果非常类似,只是鸟哥将Makefile直接给你了!但如果你执行makeinstall时,系统会告知没有install的target啊!而且版本是0.1也告知了。那么如何更新到0.2版呢?通过这个patch文件吧!这个文件的内容有点像这样:

查阅patch file 内容

[root@www main-0.1]# vim ~/main_0.1_to_0.2.patch

diff -Naur main-0.1/cos_value.c main-0.2/cos_value.c

—- main-0.1/cos_value.c  2009-06-09 22:52:33.000000000 +0800

+++ main-0.2/cos_value.c  2009-06-12 00:45:10.000000000 +0800

@@ -6,5 +6,5 @@

{

float value;

….(下面省略)….

上面有个下划线的部分,那代表使用diff比较时被比较的两个文件所在路径,这个路径非常重要。patch的基本语法如下:

patch -p 数字< patch_file

特别留意那个“-p数字”,那是与patch_file里面列出的文件名有关的信息。假如在patch_file第一行写的是这样:

*/home/guest/example/expatch.old

那么当我执行“patch -p0 < patch_file”时,则更新的文件是“/home/guest/example/expatch.old”,如果执行“patch-p1<patch_file”,则更新的文件为“home/guest/example/ expatch.old”,如果执行“patch-p4 < patch_file”则更新“expatch.old”,也就是说,-p xx那个xx代表拿掉几个斜线(/)的意思!这样可以理解了吗?好了,根据刚才上面的资料,我们可以发现比较的文件是main-0.1/xxx与main-0.2/xxx,所以说,如果你是在main-0.1下面并且想要处理更新时,就得要拿掉一个目录(因为并没有main-0.2的目录存在,我们是在当前的目录进行更新的),因此使用的是-p1才对!所以:

更新源码,并且重新编译程序

[root@www main-0.1]# patch -p1 < ../main_0.1_to_0.2.patch

patching file cos_value.c

patching file main.c

patching file Makefile

patching file sin_value.c

请注意,鸟哥目前所在目录是在 main-0.1 下面!注意与 patch 文件的相对路径!

虽然有五个文件,但其实只有四个文件有修改过!上面显示了修改过的文件!

[root@www main-0.1]# make clean main

[root@www main-0.1]# ./main

version 0.2

Please input your name: VBird

Please enter the degree angle (ex> 90): 45

Hi, Dear VBird, nice to meet you.

The sin(45.000000) is: 0.71

The cos(45.000000) is: 0.71

你可以发现,输出的结果中版本变了,输出信息多了括号 () !

[root@www main-0.1]# make install <==将它安装到 /usr/local/bin 给大家用

cp -a main /usr/local/bin

[root@www main-0.1]# main  <==直接输入命令可执行

[root@www main-0.1]# make uninstall <==删除此软件

rm -f /usr/local/bin/main

很有趣的练习吧?所以你只要下载 patch file 就能够对你的软件源码更新了。只不过更新了源码并非软件就更新,你还是得要将该软件进行编译后才会是最终正确的软件,因为patch的功能主要仅只是更新源码文件而已。切记切记!此外,如果你patch错误呢?没关系的!我们的 patch 是可以还原的。通过“patch-R < ../main_0.1_to_0.2.patch”就可以还原!

figure_0671_0385

如果我有一个很旧版的软件,这个软件已经更新到很新的版本,例如内核,那么我可以使用patchfile来更新吗?

答:这个问题挺有趣的,首先,你必须要确定旧版本与新版本之间确实有释出 patch file 才行,以 kernel 2.2.xx 及 2.4.xx 来说,这两者基本上的架构已经不同了,所以两者间是无法以 patch file 来更新的。不过,2.4.xx与2.4.yy就可以更新了。不过,因为kernel每次推出的patch文件都仅针对前一个版本而已,所以假设要由kernel 2.4.20升级到2.4.26,就必须要使用patch 2.4.21、2.4.22、2.4.23、2.4.24、2.4.25、2.4.26六个文件来“依序更新”才行。当然,如果有朋友帮你比较过2.4.20与2.4.26,那你自然就可以使用该 patch file 来直接一次更新!

22.5 函数库管理

在我们的Linux操作系统当中,函数库是很重要的一个项目,因为很多的软件之间都会互相使用彼此提供的函数库来进行特殊功能的运行,例如很多需要验证身份的程序都习惯利用 PAM 这个模块提供的验证机制来实作,而很多网络联机机制则习惯利用SSL函数库来进行联机加密的机制。所以说,函数库的利用是很重要的。不过,函数库又依照是否被编译到程序内部而分为动态与静态函数库,这两者之间有何差异?哪一种函数库比较好?下面我们就先来谈一谈!

22.5.1 动态与静态函数库

首先我们要知道的是函数库的类型有哪些,依据函数库被使用的类型而分为两大类,分别是静态(Static)与动态(Dynamic)函数库两类。下面我们来谈一谈这两种类行的函数库吧!

静态函数库的特色

扩展名(为.a)

这类的函数库通常扩展名为libxxx.a的类型。

编译行为

这类函数库在编译的时候会直接整合到执行程序当中,所以利用静态函数库编译成的文件会比较大一些。

独立执行的状态

这类函数库最大的优点就是编译成功的可执行文件可以独立执行,而不需要再向外部要求读取函数库的内容(请参照动态函数库的说明)。

升级难易度

虽然可执行文件可以独立执行,但因为函数库是直接整合到可执行文件中,因此若函数库升级时,整个可执行文件必须要重新编译才能将新版的函数库整合到程序当中。也就是说,在升级方面,只要函数库升级了,所有将此函数库纳入的程序都需要重新编译。

动态函数库的特色

扩展名(为.so)

这类函数库通常扩展名为libxxx.so的类型。

编译行为

动态函数库与静态函数库的编译行为差异挺大的。与静态函数库被整个捕捉到程序中不同,动态函数库在编译的时候,在程序里面只有一个“指向”(Pointer)的位置而已。也就是说,动态函数库的内容并没有被整合到可执行文件当中,而是当可执行文件要使用到函数库的机制时,程序才会去读取函数库来使用。由于可执行文件当中仅具有指向动态函数库所在的指标而已,并不包含函数库的内容,所以它的文件会比较小一点。

独立执行的状态

这类型的函数库所编译出来的程序不能被独立执行,因为当我们使用到函数库的机制时,程序才会去读取函数库,所以函数库文件必须要存在才行,而且,函数库的所在目录也不能改变,因为我们的可执行文件里面仅有“指标”,亦即当要取用该动态函数库时,程序会主动去某个路径下读取,所以动态函数库可不能随意移动或删除,会影响很多相关的程序软件。

升级难易度

虽然这类型的可执行文件无法独立运行,然而由于是具有指向的功能,所以,当函数库升级后,可执行文件根本不需要进行重新编译的行为,因为可执行文件会直接指向新的函数库文件(前提是函数库新旧版本的文件名相同)。

目前的 Linux distribution 比较倾向于使用动态函数库,因为如同上面提到的最重要的一点,就是函数库的升级方便。由于Linux系统里面的软件相关性太复杂了,如果使用太多的静态函数库,那么升级某一个函数库时,都会对整个系统造成很大的冲击,因为其他相关的可执行文件也要同时重新编译啊!这个时候动态函数库可就有用多了,因为只要动态函数库升级就好,其他的软件根本无须变动。

那么这些函数库放置在哪里呢?绝大多数的函数库都放置在/usr/lib、lib目录下。此外,Linux系统里面很多的函数库其实kernel就提供了,那么kernel的函数库放在哪里?呵呵!就是在/lib/modules里面!里面的数据可多着呢!不过要注意的是,不同版本的内核提供的函数库差异性是挺大的,所以kernel 2.4.xx 版本的系统不要想将内核换成2.6.xx,很容易由于函数库的不同而导致很多原本可以执行的软件无法顺利运行呢!

22.5.2 ldconfig 与/etc/ld.so.conf

在了解了动态与静态函数库,也知道我们目前的Linux大多是将函数库做成动态函数库之后,再来要知道的就是,那有没有办法增加函数库的读取性能?我们知道内存的访问速度是硬盘的好几倍,所以,如果我们将常用到的动态函数库先加载内存当中(缓存,cache),如图22-4所示,如此一来,当软件要使用动态函数库时,就不需要从头由硬盘里面读出,这样不就可以增进动态函数库的读取速度?没错,是这样的!这个时候就需要ldconfig与/etc/ld.so.conf的协助了。

如何将动态函数库加载高速缓存当中呢?

1.首先,我们必须要在/etc/ld.so.conf里面写下想要读入高速缓存当中的动态函数库所在的目录,注意,是目录而不是文件;

2.接下来则是利用ldconfig这个可执行文件将/etc/ld.so.conf的数据读入缓存当中;

3.同时也将数据记录一份在/etc/ld.so.cache这个文件当中。

figure_0673_0386

图22-4 使用 ldconfig预加载动态函数库到内存中

事实上,ldconfig还可以用来判断动态函数库的链接信息呢!赶紧利用CentOS来测试看看。假设你想要将目前你系统下的MySQL函数库加入到缓存当中时,可以这样做:

[root@www ~]# ldconfig [-f conf] [ -C cache]

[root@www ~]# ldconfig [-p]

参数:

-f conf :那个 conf 指的是某个文件名,也就是说,使用 conf 作为 libarary

函数库的取得路径,而不以 /etc/ld.so.conf 为默认值

-C cache:那个 cache 指的是某个文件名,也就是说,使用 cache 作为缓存暂存

的函数库资料,而不以 /etc/ld.so.cache 为默认值

-p :列出目前的所有函数库数据内容 (/etc/ld.so.cache 内的数据)

范例一:假设我的 MySQL 数据库函数库在 /usr/lib/mysql 当中,如何读进 cache ?

[root@www ~]# vi /etc/ld.so.conf

include ld.so.conf.d/*.conf

/usr/lib/mysql <==这一行新增的!

[root@www ~]# ldconfig <==画面上不会显示任何的信息,不要太紧张!正常的!

[root@www ~]# ldconfig -p

530 libs found in cache `/etc/ld.so.cache'

libz.so.1 (libc6) => /usr/lib/libz.so.1

libxslt.so.1 (libc6) => /usr/lib/libxslt.so.1

….(下面省略)….

 函数库名称 => 该函数库实际路径

通过上面的操作,我们可以将 MySQL 的相关函数库读入缓存当中,这样可以加快函数库读取的效率呢!在某些时候,你可能会自行加入某些Tarball安装的动态函数库,而你想要让这些动态函数库的相关链接可以被读入到缓存当中,这个时候你可以将动态函数库所在的目录名称写入/etc/ld.so.conf当中,然后执行ldconfig就可以!

22.5.3 程序的动态函数库解析:ldd

说了这么多,那么我如何判断某个可执行的二进制文件含有什么动态函数库呢?很简单,利用ldd就可以知道了!例如我想要知道/usr/bin/passwd这个程序含有的动态函数库有哪些,可以这样做:

[root@www ~]# ldd [-vdr] [filename]

参数:

-v :列出所有内容信息;

-d :重新将数据有丢失的 link 点显示出来!

-r :将 ELF 有关的错误内容显示出来!

范例一:找出 /usr/bin/passwd 这个文件的函数库数据

[root@www ~]# ldd /usr/bin/passwd

….(前面省略)….

libaudit.so.0 => /lib/libaudit.so.0 (0x00494000) <==SELinux

libselinux.so.1 => /lib/libselinux.so.1 (0x00101000) <==SELinux

libc.so.6 => /lib/libc.so.6 (0x00b99000)

libpam.so.0 => /lib/libpam.so.0 (0x004ab000)  <==PAM 模块

….(下面省略)….

我们前言的部分不是一直提到 passwd 有使用到 pam 的模块吗!怎么知道?

利用 ldd 查看一下这个文件,看到 libpam.so 了吧?这就是 pam 提供的函数库

范例二:找出 /lib/libc.so.6 这个函数的相关其他函数库!

[root@www ~]# ldd -v /lib/libc.so.6

/lib/ld-linux.so.2 (0x00ab3000)

linux-gate.so.1 => (0x00636000)

Version information: <==使用 -v 选项,增加显示其他版本信息

/lib/libc.so.6:

ld-linux.so.2 (GLIBC_PRIVATE) => /lib/ld-linux.so.2

ld-linux.so.2 (GLIBC_2.3) => /lib/ld-linux.so.2

ld-linux.so.2 (GLIBC_2.1) => /lib/ld-linux.so.2

将来如果你经常升级安装RPM的软件时(下一章节会介绍),应该经常会发现那个相关属性的问题吧?没错!我们可以先以ldd来视察相关函数库之间的相关性,以先取得了解!例如上面的例子中,我们检查了libc.so.6这个在/lib当中的函数库,结果发现它其实还跟ld-linux.so.2有关,所以我们就需要来了解一下,那个文件到底是什么软件的函数库。使用-v 这个参数还可以得知该函数库来自于哪一个软件,像上面的数据中,就可以得到该libc.so.6其实可以支持GLIBC_2.1等的版本。

22.6 检验软件正确性

前面提到很多升级与安装需要注意的事项,因为我们需要克服很多的程序漏洞,所以需要前往Linux distribution 或者是某些软件开发商的网站,下载最新并且较安全的软件文件来安装才行。好了,那么有没有可能我们下载的文件本身就有问题?是可能的!因为黑客无所不在,很多的软件开发商已经公布过他们发布的文件曾经被窜改过!那怎么办?连下载原版的数据都可能有问题了,难道没有办法判断文件的正确性吗?

这个时候我们就要通过每个文件独特的指纹验证数据了!因为每个文件的内容与文件大小都不相同,所以如果一个文件被修改之后,必然会有部分的信息不一样!利用这个东东,我们可以使用 MD5 这个指纹验证机制来判断该文件有没有被改动过。举个例子来说,下面所提供的CentOS 5.3原版光盘下载点:

http://ftp.twaren.net/Linux/CentOS/5.3/isos/i386/

同时提供了 CentOS 5.3 所有光盘/DVD 的 ISO 文件 MD5 编码,通过这个编码的比较,我们就可以得晓下载的文件是否有问题。那么万一CentOS提供的光盘影像文件被下载之后,让有心人士偷偷修改过再转到Internet上面流传,那么你下载的这个文件偏偏不是原厂提供的,你能保证该文件的内容完全没有问题吗?当然不能对不对?是的,这个时候就有md5sum与sha1sum这个文件指纹的出现!说说它的用法吧!

目前有多种机制可以计算文件的指纹码,我们选择使用较为广泛的 MD5 与SHA1 加密机制来处理。同样,我们以上面谈到的 CentOS 5.3 的网络安装镜像文件来处理试看看好了。在上面的链接网址上面,你会看到几个文件:

CentOS-5.3-i386-netinstall.iso:CentOS 5.3 的网络安装镜像文件;

md5sum.txt:MD5指纹编码;

sha1sum.txt:SHA1指纹编码。

如果你下载了CentOS-5.3-i386-netinstall.iso后,再以md5sum与sha1sum去检验这个文件时,文件所回传的指纹码应该要与网站上面提供的文件指纹码相同才对!我们由网站上面提供的指纹码知道这个镜像文件的指纹为:

MD5: 6ae4077a9fc2dcedca96013701bd2a43

SHA1: a0c640ae0c68cc0d9558cf4f8855f24671b3dadb

[root@www ~]# md5sum/sha1sum [-bct] filename

[root@www ~]# md5sum/sha1sum [—status|—warn] —check filename

参数:

-b :使用二进制的读取方式,默认为 Windows/DOS 文件类型的读取方式;

-c :检验文件指纹;

-t :以文本类型来读取文件指纹。

范例一:将刚才的文件下载后,测试看看指纹码

[root@www ~]# wget \

> http://ftp.twaren.net/Linux/CentOS/5.3/isos/i386/CentOS-5.3-i386-netinstall.iso

[root@www ~]# md5sum CentOS-5.3-i386-netinstall.iso

6ae4077a9fc2dcedca96013701bd2a43 CentOS-5.3-i386-netinstall.iso

[root@www ~]# sha1sum CentOS-5.3-i386-netinstall.iso

a0c640ae0c68cc0d9558cf4f8855f24671b3dadb CentOS-5.3-i386-netinstall.iso

看!显示的编码是否与上面相同呢?赶紧测试看看!

一般而言,每个系统里面的文件内容大概都不相同,例如你的系统中的/etc/passwd 这个登录信息文件与我的一定不一样,因为我们的用户与密码、Shell 及目录等大概都不相同,所以由 md5sum这个文件指纹分析程序所自行计算出来的指纹表当然就不相同!

好了,那么如何应用这个东西呢?基本上,你必须要在你的Linux系统上为你的这些重要的文件进行指纹数据库的创建(好像在做户口调查),将下面这些文件新建数据库:

/etc/passwd

/etc/shadow((假如你不让用户改密码了)

/etc/group

/usr/bin/passwd

/sbin/portmap

/bin/login(这个也很容易被黑客利用)

/bin/ls

/bin/ps

/usr/bin/top

这几个文件最容易被修改了!因为很多木马程序执行的时候,还是会有所谓的“线程,PID”为了怕被root追查出来,所以它们都会修改这些检查进程的文件,如果你可以为这些文件新建指纹数据库(就是使用 md5sum 检查一次,将该文件指纹记录下来,然后经常以 shell script 的方式由程序自行来检查指纹表是否不同了),那么对于文件系统会比较安全。

22.7 重点回顾

源码其实大多是纯文本文件,需要通过编译程序的编译操作后才能够制作出Linux系统能够识别的可执行的二进制文件。

开放源码可以加速软件的更新速度,让软件性能更快、漏洞修补更实时。

在Linux系统当中,最标准的C语言编译程序为gcc。

在编译的过程当中,可以通过其他软件提供的函数库来使用该软件的相关机制与功能。

为了简化编译过程当中的复杂的命令输入,可以通过make与makefile规则定义来简化程序的更新、编译与链接等操作。

Tarball为使用tar与gzip/bzip2压缩功能所打包与压缩的具有源码的文件。

一般而言,要使用Tarball管理Linux系统上的软件,最好需要gcc、make、autoconfig、kernelsource、kernel header等软件才行,所以在安装 Linux 之初,最好就能够选择 Software development 以及kernel development 之类的组。

函数库有动态函数库与静态函数库之分,动态函数库在升级上具有较佳的优势。动态函数库的扩展名为.so,而静态函数库则是.a。

patch的主要功能是更新源码,所以更新源码之后,还需要进行重新编译的操作才行。

可以利用ldconfig与/etc/ld.so.conf来制作动态函数库的链接与缓存。

通过MD5的编码可以判断下载的文件是否为原本厂商所发布的文件。

22.8 本章习题

练习题部分

请前往企鹅游戏网站 http://xpenguins.seul.org/下载 xpenguins-2.2.tar.gz 源码文件,并安装该软件。安装完毕之后,请在GNOME图形界面执行xpenguins,看看有没有出现如同官网上面出现的小企鹅?

情境模拟题部分

请依照下面的方式来建立你的系统的重要文件指纹码。

1.将/etc/{passwd,shadow,group}以及系统上面所有的 SUID/SGID 文件创建文件列表,该列表文件名为“important.file”。

[root@www ~]# ls /etc/{passwd,shadow,group} > important.file

[root@www ~]# find /bin /sbin /usr/sbin /usr/bin -perm +6000 \

> >> important.file

2.通过这个文件名列表,以名为 md5.checkfile.sh 的文件名去新建指纹码,并将该指纹码文件“finger1.file”设置成为不可修改的属性。

[root@www ~]# vim md5.checkfile.sh

!/bin/bash

for filename in $(cat important.file)

do

md5sum $filename >> finger1.file

done

[root@www ~]# sh md5.checkfile.sh

[root@www ~]# chattr +i finger1.file

3.通过相同的机制去新建后续的分析数据为finger_new.file,并将两者进行比较,若有问题则提供email给root查阅:

[root@www ~]# vim md5.checkfile.sh

!/bin/bash

if [ "$1" == "new" ]; then

for filename in $(cat important.file)

do

md5sum $filename >> finger1.file

done

echo "New file finger1.file is created."

exit 0

fi

if [ ! -f finger1.file ]; then

echo "file: finger1.file NOT exist."

exit 1

fi

[ -f finger_new.file ] && rm finger_new.file

for filename in $(cat important.file)

do

md5sum $filename >> finger_new.file

done

testing=$(diff finger1.file finger_new.file)

if [ "$testing" != "" ]; then

diff finger1.file finger_new.file | mail -s 'finger trouble..' root

fi

[root@www ~]# vim /etc/crontab

30 2 * root cd /root; sh md5.checkfile.sh

如此一来,每天系统会主动去分析你认为重要文件的指纹数据,然后再加以分析,看看有没有被改动过。不过,如果该变动是正常的,例如CentOS自动升级时,那么你就得要删除finger1.file,再重新建置一个新的指纹数据库才行,否则你会每天收到有问题信件的回报。

22.9 参考数据与扩展阅读

几种常见加密机制的全名:

md5(Message-Digest algorithm 5)http://en.wikipedia.org/wiki/MD5

sha(Secure Hash Algorithm)http://en.wikipedia.org/wiki/SHA_hash_functions

des(Data Encryption Standard)http://en.wikipedia.org/wiki/Data_Encryption_Standard

洪朝贵老师的C程序语言教程:http://www.cyut.edu.tw/~ckhung/b/c/