第二十二章、软体安装:原始码与 Tarball
最近更新日期:2009/09/15
1. 开放源码的软体安装与升级简介
1.1 什么是开放源码、编译器与可执行档 1.2 什么是函式库 1.3 什么是 make 与 configure 1.4 什么是 Tarball 的软体 1.5 如何安装与升级软体 2. 使用传统程式语言进行编译的简单范例 2.1 单一程式:印出 Hello World 2.2 主、副程式连结:副程式的编译 2.3 呼叫外部函式库:加入连结的函式库 2.4 gcc 的简易用法 (编译、参数与链结) 3. 用 make 进行巨集编译 3.1 为什么要用 make 3.2 makefile 的基本语法与变数 4. Tarball 的管理与建议 4.1 使用原始码管理软体所需要的基础软体 4.2 Tarball 安装的基本步骤 4.3 一般 Tarball 软体安装的建议事项 (如何移除?升级?) 4.4 一个简单的范例、利用 ntp 来示范 4.5 利用 patch 更新原始码 5. 函式库管理 5.1 动态与静态函式库 5.2 ldconfig 与 /etc/ld.so.conf 5.3 程式的动态函式库解析: ldd 6. 检验软体的正确性 6.1 md5sum / sha1sum 7. 重点回顾 8. 课后练习 9. 参考资料与延伸阅读 10. 针对本文的建议:http://phorum.vbird.org/viewtopic.php?t=23892 开放源码的软体安装与升级简介 如果鸟哥想要在我的 Linux 伺服器上面跑网页伺服器 (WWW server) 这项服务,那么我应该要做些什么事呢?当然就一定需要‘安装网页伺服器的软体’啰!如果鸟哥的伺服器上面没有这个软体的话,那当然也就无法启用 WWW 的服务啦!所以啦,想要在你的 Linux 上面进行一些有的没的功能,学会‘如何安装软体’是很重要的一个课题! 咦!安装软体有什么难的?在 W 牌的作业系统上面安装软体时,不是只要一直给他按 ‘下一步’就可以安装妥当了吗?话是这样说没错啦,不过,也由于如此,所以在 Windows 系统上面的软体都是一模一样的,也就是说,你‘无法修改该软体的原始程式码’,因此, 万一你想要增加或者减少该软体的某些功能时,大概只能求助于当初发行该软体的厂商了!(这就是所谓的商机吗?) 或许你会说:‘唉呦!我不过是一般人,不会用到多余的功能,所以不太可能会更动到程式码的部分吧?’ 如果你这么想的话,很抱歉~是有问题的!怎么说呢?像目前网路上面的病毒、黑客软体、臭虫程式等等, 都可能对你的主机上面的某些软体造成影响,导致主机的当机或者是其他资料损毁等等的伤害。 如果你可以藉由安全资讯单位所提供的修订方式进行修改, 那么你将可以很快速的自行修补好该软体的漏洞,而不必一定要等到软体开发商提供修补的程式包哩!要知道,提早补洞是很重要的一件事。
这样说可以了解 Linux 的优点了吗?没错!因为 Linux
上面的软体几乎都是经过 GPL 的授权,所以每个软体几乎均提供原始程式码,
并且你可以自行修改该程式码,以符合你个人的需求呢!很棒吧!这就是开放源码的优点啰!不过,到底什么是开放源码?
这些程式码是什么咚咚?又 Linux 上面可以执行的相关软体档案与开放源码之间是如何转换的?不同版本的
Linux 之间能不能使用同一个执行档?或者是该执行档需要由原始程式码的部分重新进行转换?
这些都是需要厘清观念的。底下我们先就原始程式码与可执行档来进行说明。 什么是开放源码、编译器与可执行档 在讨论程式码是什么之前,我们先来谈论一下什么是可执行档?我们说过,在 Linux 系统上面,一个档案能不能被执行看的是有没有可执行的那个权限 (具有 x permission),不过,Linux 系统上真正认识的可执行档其实是二进位档案 ( binary program),例如 /usr/bin/passwd, /bin/touch 这些个档案即为二进位程式码。 或许你会说 shell scripts 不是也可以执行吗?其实 shell scripts 只是利用 shell (例如 bash) 这支程式的功能进行一些判断式,而最终执行的除了 bash 提供的功能外,仍是呼叫一些已经编译好的二进位程式来执行的呢! 当然啦, bash 本身也是一支二进位程式啊!那么我怎么知道一个档案是否为 binary 呢?还记得我们在第七章里面提到的 file 这个指令的功能吗?对啦!用他就是了!我们现在来测试一下:
看到了吧!如果是 binary 而且是可以执行的时候,他就会显示执行档类别 (ELF 32-bit LSB executable), 同时会说明是否使用动态函式库 (shared libs),而如果是一般的 script ,那他就会显示出 text executables 之类的字样!
既然 Linux 作业系统真正认识的其实是 binary program,那么我们是如何做出这样的一支 binary 的程式呢?首先,我们必须要写程式,用什么东西写程式?就是一般的文书处理器啊!鸟哥都喜欢使用 vim 来进行程式的撰写,写完的程式就是所谓的原始程式码啰! 这个程式码档案其实就是一般的纯文字档。 在完成这个原始码档案的编写之后,再来就是要将这个档案‘编译’成为作业系统看的懂得 binary program 啰!而要编译自然就需要‘编译器’来动作, 经过编译器的编译与连结之后,就会产生一支可以执行的 binary program 啰。 举个例子来说,在 Linux 上面最标准的程式语言为 C ,所以我使用 C 的语法进行原始程式码的书写,写完之后,以 Linux 上标准的 C 语言编译器 gcc 这支程式来编译,就可以制作一支可以执行的 binary program 啰。整个的流程有点像这样: 图 1.1.1、利用 gcc 编译器进行程式的编译流程示意图" 事实上,在编译的过程当中还会产生所谓的目标档 (Object file),这些档案是以 *.o 的副档名样式存在的!至于 C 语言的原始码档案通常以 *.c 作为副档名。此外,有的时候,我们会在程式当中‘引用、呼叫’ 其他的外部副程式,或者是利用其他软体提供的‘函数功能’,这个时候,我们就必须要在编译的过程当中, 将该函式库给他加进去,如此一来,编译器就可以将所有的程式码与函式库作一个连结 (Link) 以产生正确的执行档啰。 总之,我们可以这么说:
什么是函式库 在前一小节的图1.1.1示意图中,在编译的过程里面有提到函式库这东西。 什么是函式库呢?先举个例子来说:我们的 Linux 系统上通常已经提供一个可以进行身份验证的模组, 就是在第十四章提到的 PAM 模组。这个 PAM 提供的功能可以让很多的程式在被执行的时候,除了可以验证使用者登入的资讯外, 还可以将身份确认的资料记录在登录档里面,以方便系统管理员的追踪! 既然有这么好用的功能,那如果我要编写具有身份认证功能的程式时,直接引用该 PAM 的功能就好啦,如此一来,我就不需要重新设计认证机制啰!也就是说,只要在我写的程式码里面,设定去呼叫 PAM 的函式功能,我的程式就可以利用 Linux 原本就有的身份认证的程序咯!除此之外,其实我们的 Linux 核心也提供了相当多的函式库来给硬体开发者利用喔。 函式库又分为动态与静态函式库,这两个咚咚的分别我们在后面的小节再加以说明。 这里我们以一个简单的流程图,来示意一支有呼叫外部函式库的程式的执行情况。 图 1.2.1、程式执行时引用外部动态函式库的示意图 很简单的示意图啊!^_^!而如果要在程式里面加入引用的函式库,就需要如图 1.1.1 所示, 亦即在编译的过程当中,就需要加入函式库的相关设定啰。 事实上, Linux 的核心提供很多的核心相关函式库与外部参数, 这些核心功能在设计硬体的驱动程式的时候是相当有用的资讯,这些核心相关资讯大多放置在 /usr/include, /lib, /usr/lib 里面哩!我们在本章的后续小节再来探讨。反正我们可以简单的这么想:
什么是 make 与 configure 事实上,使用类似 gcc 的编译器来进行编译的过程并不简单,因为一套软体并不会仅有一支程式, 而是有一堆程式码档案。所以除了每个主程式与副程式均需要写上一笔编译过程的指令外,还需要写上最终的连结程序。 程式码小的时候还好,如果是类似 WWW 伺服器软体 (例如 Apache) ,或者是类似核心的原始码,动则数百 MBytes 的资料量,编译指令会写到疯掉~这个时候,我们就可以使用 make 这个指令的相关功能来进行编译过程的指令简化了! 当执行 make 时,make 会在当时的目录下搜寻 Makefile (or makefile) 这个文字档,而 Makefile 里面则记录了原始码如何编译的详细资讯! make 会自动的判别原始码是否经过变动了,而自动更新执行档,是软体工程师相当好用的一个辅助工具呢! 咦!make 是一支程式,会去找 Makefile ,那 Makefile 怎么写? 通常软体开发商都会写一支侦测程式来侦测使用者的作业环境, 以及该作业环境是否有软体开发商所需要的其他功能,该侦测程式侦测完毕后,就会主动的建立这个 Makefile 的规则档案啦!通常这支侦测程式的档名为 configure 或者是 config 。 咦!那为什么要侦测作业环境呢?在第一章当中, 不是曾经提过其实每个 Linux distribution 都使用同样的核心吗?但你得要注意, 不同版本的核心所使用的系统呼叫可能不相同,而且每个软体所需要的相依的函式库也不相同, 同时,软体开发商不会仅针对 Linux 开发,而是会针对整个 Unix-Like 做开发啊! 所以他也必须要侦测该作业系统平台有没有提供合适的编译器才行!所以当然要侦测环境啊! 一般来说,侦测程式会侦测的资料大约有底下这些:
至于 make 与 configure 运作流程的相关性,我们可以使用底下的图示来示意一下啊! 下图中,你要进行的任务其实只有两个,一个是执行 configure 来建立 Makefile , 这个步骤一定要成功!成功之后再以 make 来呼叫所需要的资料来编译即可!非常简单! 图 1.3.1、透过 configure 与 make 进行编译示意图 由于不同的 Linux distribution 的函式库档案所放置的路径,或者是函式库的档名订定, 或者是预设安装的编译器,以及核心的版本都不相同,因此理论上,你无法在 CentOS 5.x 上面编译出 binary program 后,还将他拿到 SuSE 上面执行,这个动作通常是不可能成功的! 因为呼叫的目标函式库位置可能不同 (参考图1.2.1) , 核心版本更不可能相同!所以能够执行的情况是微乎其微!所以同一套软体要在不同的平台上面执行时, 必须要重复编译!所以才需要原始码嘛!了解乎! 详细的 make 用法与 Makefile 规则,在后续的小节里面再探讨啰! 什么是 Tarball 的软体 从前面几个小节的说明来看,我们知道所谓的原始程式码,其实就是一些写满了程式码的纯文字档案。 那我们在第九章压缩指令的介绍当中, 也了解了纯文字档在网路上其实是很浪费频宽的一种档案格式! 所以啦,如果能够将这些原始码透过档案的打包与压缩技术来将档案的数量与容量减小, 不但让使用者容易下载,软体开发商的网站频宽也能够节省很多很多啊!这就是 Tarball 档案的由来啰!
所谓的 Tarball 档案,其实就是将软体的所有原始码档案先以 tar 打包,然后再以压缩技术来压缩,通常最常见的就是以 gzip 来压缩了。因为利用了 tar 与 gzip 的功能,所以 tarball 档案一般的副档名就会写成 *.tar.gz 或者是简写为 *.tgz 啰!不过,近来由于 bzip2 的压缩率较佳,所以 Tarball 渐渐的以 bzip2 的压缩技术来取代 gzip 啰!因此档名也会变成 *.tar.bz2 之类的哩。所以说, Tarball 是一个软体包, 你将他解压缩之后,里面的档案通常就会有:
其中最重要的是那个 INSTALL 或者是 README 这两个档案,通常你只要能够参考这两个档案, Tarball 软体的安装是很简单的啦!我们在后面的章节会再继续介绍 Tarball 这个玩意儿。 如何安装与升级软体 将原始码作了一个简单的介绍,也知道了系统其实认识的可执行档是 binary program 之后,好了,得要聊一聊,那么怎么安装与升级一个 Tarball 的软体?为什么要安装一个新的软体呢?当然是因为我们的主机上面没有该软体啰!那么, 为何要升级呢?原因可能有底下这些:
在上面的需求当中,尤其需要注意的是第二点,当一个软体有安全上的顾虑时,千万不要怀疑, 赶紧更新软体吧!否则造成网路危机,那可不是闹着玩的!那么更新的方法有哪些呢? 基本上更新的方法可以分为两大类,分别是:
上面第一点很简单,就是直接以 Tarball 在自己的机器上面进行侦测、编译、 安装与设定等等动作来升级就是了。不过,这样的动作虽然让使用者在安装过程当中具有很高的弹性, 但毕竟是比较麻烦一点,如果 Linux distribution 厂商能够针对自己的作业平台先进行编译等过程,再将编译好的 binary program 释出的话,那由于我的系统与该 Linux distribution 的环境是相同的,所以他所释出的 binary program 就可以在我的机器上面直接安装啦!省略了侦测与编译等等繁杂的过程呢! 这个预先编译好程式的机制存在于很多 distribution 喔,包括有 Red Hat 系统 (含 Fedora/CentOS 系列) 发展的 RPM 软体管理机制与 yum 线上更新模式; Debian 使用的 dpkg 软体管理机制与 APT 线上更新模式等等。 由于 CentOS 系统是依循标准的 Linux distribution,所以可以使用 Tarball 直接进行编译的安装与升级, 当然也可以使用 RPM 相关的机制来进行安装与升级啰!本章节主要针对 Tarball ,至于 RPM 则留待下个章节再来介绍呢! 好了,那么一个软体的 Tarball 是如何安装的呢?基本流程是这样的啦:
上面第 3, 4 步骤当中,我们可以透过 make 这个指令的功能来简化他, 所以整个步骤其实是很简单的啦!只不过你就得需要至少有 gcc 以及 make 这两个软体在你的 Linux 系统里面才行喔! 详细的过程以及需要的软体我们在后面的章节继续来介绍的啦! 使用传统程式语言进行编译的简单范例 经过上面的介绍之后,你应该比较清楚的知道原始码、编译器、函式库与执行档之间的相关性了。
不过,详细的流程可能还是不很清楚,所以,在这里我们以一个简单的程式范例来说明整个编译的过程喔!赶紧进入
Linux 系统,实地的操作一下底下的范例呢! 单一程式:印出 Hello World 我们以 Linux 上面最常见的 C 语言来撰写第一支程式!第一支程式最常作的就是..... 在荧幕上面印出‘Hello World!’的字样~当然, 这里我们是以简单的 C 语言来撰写,如果你对于 C 有兴趣的话,那么请自行购买相关的书籍喔! ^_^ 好了,不啰唆,立刻编辑第一支程式吧!
上面是用 C 语言的语法写成的一个程式档案。第一行的那个‘ # ’并不是注解喔!如果你担心输入错误, 请到底下的连结下载这个档案:
在预设的状态下,如果我们直接以 gcc 编译原始码,并且没有加上任何参数,则执行档的档名会被自动设定为 a.out 这个档案名称! 所以你就能够直接执行 ./a.out 这个执行档啦!上面的例子很简单吧!那个 hello.c 就是原始码,而 gcc 就是编译器,至于 a.out 就是编译成功的可执行 binary program 啰! 咦!那如果我想要产生目标档 (object file) 来进行其他的动作,而且执行档的档名也不要用预设的 a.out ,那该如何是好?其实你可以将上面的第 2 个步骤改成这样:
这个步骤主要是利用 hello.o 这个目标档制作出一个名为 hello 的执行档,详细的 gcc 语法我们会在后续章节中继续介绍!透过这个动作后,我们可以得到 hello 及 hello.o 两个档案, 真正可以执行的是 hello 这个 binary program 喔! 或许你会觉得,咦!只要一个动作作出 a.out 就好了,干嘛还要先制作目标档再做成执行档呢? 呵呵!透过下个范例,你就可以知道为什么啦! 主、副程式连结:副程式的编译 如果我们在一个主程式里面又呼叫了另一个副程式呢?这是很常见的一个程式写法,
因为可以简化整个程式的易读性!在底下的例子当中,我们以
thanks.c 这个主程式去呼叫 thanks_2.c 这个副程式,写法很简单:
上面这两个档案你可以到底下下载:
知道为什么要制作出目标档了吗?由于我们的原始码档案有时并非仅只有一个档案,所以我们无法直接进行编译。 这个时候就需要先产生目标档,然后再以连结制作成为 binary 可执行档。另外,如果有一天,你更新了 thanks_2.c 这个档案的内容,则你只要重新编译 thanks_2.c 来产生新的 thanks_2.o ,然后再以连结制作出新的 binary 可执行档即可!而不必重新编译其他没有更动过的原始码档案。 这对于软体开发者来说,是一个很重要的功能,因为有时候要将偌大的原始码全部编译完成,会花很长的一段时间呢! 此外,如果你想要让程式在执行的时候具有比较好的效能,或者是其他的除错功能时, 可以在编译的过程里面加入适当的参数,例如底下的例子:
至于更多的 gcc 额外参数功能,就得要 man gcc 啰~呵呵!可多的跟天书一样~ 呼叫外部函式库:加入连结的函式库 刚刚我们都仅只是在荧幕上面印出一些字眼而已,如果说要计算数学公式呢?例如我们想要计算出三角函数里面的 sin (90度角)。要注意的是,大多数的程式语言都是使用径度而不是一般我们在计算的‘角度’, 180 度角约等于 3.14 径度!嗯!那我们就来写一下这个程式吧!
上面这个档案的内容可以在底下取得! 那要如何编译这支程式呢?我们先直接编译看看:
特别注意上面的错误讯息,唉啊!怎么没有编译成功?它说的是‘undefined
reference to sin’,说的是‘没有 sin
的相关定义参考值!’,为什么会这样呢?这是因为 C 语言里面的 sin 函示是写在 libm.so
这个函式库中,而我们并没有在原始码里面将这个函式库功能加进去,
所以当然就需要在编译与连结的时候将这个函式库给他连结进执行档里面啊!我们可以这样做:
特别注意,使用 gcc 编译时所加入的那个 -lm 是有意义的,他可以拆开成两部份来看:
所以 -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 档案放置的目录:
-I/path 后面接的路径( Path )就是设定要去搜寻相关的 include 档案的目录啦!不过,同样的,预设值是放置在 /usr/include 底下,除非你的 include 档案放置在其他路径,否则也可以略过这个项目! 透过上面的几个小范例,你应该对于 gcc 以及原始码有一定程度的认识了,再接下来,我们来稍微整理一下 gcc 的简易使用方法吧! gcc 的简易用法 (编译、参数与链结) 前面说过, gcc 为 Linux 上面最标准的编译器,这个 gcc 是由 GNU 计划所维护的,有兴趣的朋友请自行前往参考。既然 gcc 对于 Linux 上的 Open source 是这么样的重要,所以底下我们就列举几个 gcc 常见的参数,如此一来大家应该更容易了解原始码的各项功能吧!
比较重要的大概就是这一些。另外,我们通常称 -Wall 或者 -O 这些非必要的参数为旗标 (FLAGS),因为我们使用的是 C 程式语言,所以有时候也会简称这些旗标为 CFLAGS ,这些变数偶尔会被使用的喔!尤其是在后头会介绍的 make 相关的用法时,更是重要的很呐! ^_^ 用 make 进行巨集编译 在本章一开始我们提到过 make 的功能是可以简化编译过程里面所下达的指令,同时还具有很多很方便的功能!那么底下咱们就来试看看使用
make 简化下达编译指令的流程吧! 为什么要用 make 先来想像一个案例,假设我的执行档里面包含了四个原始码档案,分别是 main.c haha.c sin_value.c cos_value.c 这四个档案,这四个档案的目的是:
这四个档案你可以到 http://linux.vbird.org/linux_basic/0520source/main.tgz 来下载。由于这四个档案里面包含了相关性,并且还用到数学函式在里面,所以如果你想要让这个程式可以跑, 那么就需要这样编译:
编译的过程需要进行好多动作啊!而且如果要重新编译,则上述的流程得要重新来一遍,光是找出这些指令就够烦人的了! 如果可以的话,能不能一个步骤就给他完成上面所有的动作呢?那就利用 make 这个工具吧! 先试看看在这个目录下建立一个名为 makefile 的档案,内容如下:
或许你会说:‘如果我建立一个 shell script 来将上面的所有动作都集结在一起,不是具有同样的效果吗?’呵呵! 效果当然不一样,以上面的测试为例,我们仅写出 main 需要的目标档,结果 make 会主动的去判断每个目标档相关的原始码档案,并直接予以编译,最后再直接进行连结的动作! 真的是很方便啊!此外,如果我们更动过某些原始码档案,则 make 也可以主动的判断哪一个原始码与相关的目标档档案有更新过, 并仅更新该档案,如此一来,将可大大的节省很多编译的时间呢!要知道,某些程式在进行编译的行为时,会消耗很多的 CPU 资源呢!所以说, make 有这些好处:
既然 make 有这么多的优点,那么我们当然就得好好的了解一下 make 这个令人关心的家伙啦!而 make 里面最需要注意的大概就是那个规则档案,也就是 makefile 这个档案的语法啦!所以底下我们就针对 makefile 的语法来加以介绍啰。 makefile 的基本语法与变数 make 的语法可是相当的多而复杂的,有兴趣的话可以到 GNU (注1) 去查阅相关的说明,鸟哥这里仅列出一些基本的规则,重点在于让读者们未来在接触原始码时,不会太紧张啊! 好了,基本的 makefile 规则是这样的:
那个标的 (target) 就是我们想要建立的资讯,而目标档就是具有相关性的 object files ,那建立执行档的语法就是以 <tab> 按键开头的那一行!特别给他留意喔,‘命令列必须要以 tab 按键作为开头’才行!他的规则基本上是这样的:
同样的,我们以刚刚上一个小节的范例进一步说明,如果我想要有两个以上的执行动作时, 例如下达一个指令就直接清除掉所有的目标档与执行档,该如何制作呢?
如此一来,我们的 makefile 里面就具有至少两个标的,分别是 main 与 clean ,如果我们想要建立 main 的话,输入‘make main’,如果想要清除有的没的,输入‘make clean’即可啊!而如果想要先清除目标档再编译 main 这个程式的话,就可以这样输入:‘make clean main’,如下所示:
这样就很清楚了吧!但是,你是否会觉得,咦! makefile 里面怎么重复的资料这么多啊!没错!所以我们可以再藉由 shell script 那时学到的‘变数’来更简化 makefile 喔:
与 bash shell script 的语法有点不太相同,变数的基本语法为:
由于 gcc 在进行编译的行为时,会主动的去读取 CFLAGS 这个环境变数,所以,你可以直接在 shell 定义出这个环境变数,也可以在 makefile 档案里面去定义,更可以在指令列当中给予这个咚咚呢!例如:
也可以这样:
咦!我可以利用指令列进行环境变数的输入,也可以在档案内直接指定环境变数,那万一这个 CFLAGS 的内容在指令列与 makefile 里面并不相同时,以那个方式输入的为主?呵呵!问了个好问题啊! 环境变数取用的规则是这样的:
此外,还有一些特殊的变数需要了解的喔:
所以我也可以将 makefile 改成:
这样是否稍微了解了 makefile (也可能是 Makefile) 的基本语法?这对于你未来自行修改原始码的编译规则时,是很有帮助的喔!^_^! Tarball 的管理与建议 在我们知道了原始码的相关资讯之后,再来要了解的自然就是如何使用具有原始码的 Tarball 来建立一个属于自己的软体啰!从前面几个小节的说明当中,我们晓得其实 Tarball 的安装是可以跨平台的,因为 C 语言的程式码在各个平台上面是可以共通的, 只是需要的编译器可能并不相同而已。例如 Linux 上面用 gcc 而 Windows 上面也有相关的 C 编译器啊~所以呢,同样的一组原始码,既可以在 CentOS Linux 上面编译,也可以在 SuSE Linux 上面编译,当然,也可以在大部分的 Unix 平台上面编译成功的! 如果万一没有编译成功怎么办?很简单啊,透过修改小部分的程式码 (通常是因为很小部分的异动而已) 就可以进行跨平台的移植了!也就是说,刚刚我们在 Linux 底下写的程式‘理论上,是可以在 Windows 上面编译的!’这就是原始码的好处啦!所以说,如果朋友们想要学习程式语言的话, 鸟哥个人是比较建议学习‘具有跨平台能力的程式语言’,例如 C 就是很不错的一个! 唉啊!又扯远了~赶紧拉回来继续说明我们的 Tarball 啦! 使用原始码管理软体所需要的基础软体 从原始码的说明我们晓得要制作一个 binary program 需要很多咚咚的呢!这包括底下这些基础的软体:
虽然 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 的软体群组安装功能,你可以这样做:
大概就是这样,更多的资讯请参考下一章的介绍喔。 Tarball 安装的基本步骤 我们提过以 Tarball 方式释出的软体是需要重新编译可执行的 binary program 的。而 Tarball 是以 tar 这个指令来打包与压缩的档案,所以啦,当然就需要先将 Tarball 解压缩,然后到原始码所在的目录下进行 makefile 的建立,再以 make 来进行编译与安装的动作啊!所以整个安装的基础动作大多是这样的:
注意到上面的第二个步骤,通常在每个软体在释出的时候,都会附上 INSTALL 或者是 README 这种档名的说明档,这些说明档请‘确实详细的’ 阅读过一遍,通常这些档案会记录这个软体的安装要求、软体的工作项目、 与软体的安装参数设定及技巧等,只要仔细的读完这些档案,基本上,要安装好 tarball 的档案,都不会有什么大问题啰。 至于 makefile 在制作出来之后,里头会有相当多的标的 (target),最常见的就是 install 与 clean 啰!通常‘make clean’代表着将目标档 (object file) 清除掉,‘make’则是将原始码进行编译而已。 注意喔!编译完成的可执行档与相关的设定档还在原始码所在的目录当中喔!因此,最后要进行‘make install’来将编译完成的所有咚咚都给他安装到正确的路径去,这样就可以使用该软体啦! OK!我们底下约略提一下大部分的 tarball 软体之安装的指令下达方式:
请注意,上面的步骤是一步一步来进行的,而其中只要一个步骤无法成功,那么后续的步骤就完全没有办法进行的! 因此,要确定每一的步骤都是成功的才可以!举个例子来说,万一今天你在 ./configure 就不成功了,那么就表示 Makefile 无法被建立起来,要知道,后面的步骤都是根据 Makefile 来进行的,既然无法建立 Makefile,后续的步骤当然无法成功啰! 另外,如果在 make 无法成功的话,那就表示原始档案无法被编译成可执行档,那么 make install 主要是将编译完成的档案给他放置到档案系统中的,既然都没有可用的执行档了,怎么进行安装? 所以啰,要每一个步骤都正确无误才能往下继续做!此外,如果安装成功, 并且是安装在独立的一个目录中,例如 /usr/local/packages 这个目录中好了,那么你就必需手动的将这个软体的 man page 给他写入 /etc/man.config 里面去。 一般 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, lib, bin, man 等目录当中,分别代表‘设定档、函式库、执行档、线上说明档’。 好了,那么你是以 tarball 来安装时呢?如果是放在预设的 /usr/local 里面,由于 /usr/local 原本就预设这几个目录了,所以你的资料就会被放在:
但是如果你每个软体都选择在这个预设的路径下安装的话, 那么所有的软体的档案都将放置在这四个目录当中,因此,如果你都安装在这个目录下的话, 那么未来再想要升级或移除的时候,就会比较难以追查档案的来源啰! 而如果你在安装的时候选择的是单独的目录,例如我将 apache 安装在 /usr/local/apache 当中,那么你的档案目录就会变成:
呵呵!单一软体的档案都在同一个目录之下,那么要移除该软体就简单的多了! 只要将该目录移除即可视为该软体已经被移除啰!以上面为例,我想要移除 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 的管理,通常鸟哥会这样建议使用者:
一个简单的范例、利用 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 假设我对这个软体的要求是这样的:
那么你可以依照底下的步骤来安装测试看看 (如果可以的话,请你不要参考底下的文件资料,
先自行安装过一遍这个软体,然后再来对照一下鸟哥的步骤喔!)。
一般来说 configure 设定参数较重要的就是那个 --prefix=/path 了,--prefix 后面接的路径就是‘这个软体未来要安装到那个目录去?’如果你没有指定 --prefix=/path 这个参数,通常预设参数就是 /usr/local 至于其他的参数意义就得要参考 ./configure --help 了! 这个动作完成之后会产生 makefile 或 Makefile 这个档案。当然啦,这个侦测检查的过程会显示在荧幕上, 特别留意关于 gcc 的检查,还有最重要的是最后需要成功的建立起 Makefile 才行!
整个动作就这么简单,你完成了吗?完成之后到 /usr/local/ntp 你发现了什么? 利用 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 这个档案来更新原本的旧程式呢? 事实上,当我们发现一些软体的漏洞,通常是某一段程式码写的不好所致。因此, 所谓的‘更新原始码’常常是只有更改部分档案的小部分内容而已。既然如此的话, 那么我们是否可以就那些被更动的档案来进行修改就可以咯?也就是说, 旧版本到新版本间没有更动过的档案就不要理他,仅将有修订过的档案部分来处理即可。 这有什么好处呢?首先,没有更动过的档案的目标档 (object file) 根本就不需要重新编译,而且有更动过的档案又可以利用 make 来自动 update (更新),如此一来,我们原先的设定 (makefile 档案里面的规则) 将不需要重新改写或侦测!可以节省很多宝贵的时间呢 (例如后续章节会提到的核心的编译!) 从上面的说明当中,我们可以发现,如果可以将旧版的原始码资料改写成新版的版本, 那么就能直接编译了,而不需要将全部的新版 Tarball 重新下载一次呢!可以节省频宽与时间说!那么如何改写原始码? 难道要我们一个档案一个档案去参考然后修订吗?当然没有这么没人性! 我们在第十二章、正规表示法的时候有提到一个比对档案的指令,那就是 diff,这个指令可以将‘两个档案之间的差异性列出来’呢!那我们也知道新旧版本的档案之间, 其实只有修改一些程式码而已,那么我们可以透过 diff 比对出新旧版本之间的文字差异,然后再以相关的指令来将旧版的档案更新吗? 呵呵!当然可以啦!那就是 patch 这个指令啦!很多的软体开发商在更新了原始码之后,几乎都会释出所谓的 patch file,也就是直接将原始码 update 而已的一个方式喔!我们底下以一个简单的范例来说明给你了解喔! 关于 diff 与 patch 的基本用法我们在第十二章都谈过了,所以这里不再就这两个指令的语法进行介绍, 请回去参阅第十二章的内容。这里我们来举个案例解释一下好了。假设我们刚刚计算三角函数的程式 (main) 历经多次改版, 0.1 版仅会简单的输出, 0.2 版的输出就会含有角度值,因此这两个版本的内容不相同。如下所示,两个档案的意义为:
请您先下载这两个档案,并且解压缩到你的 /root 底下。你会发现系统产生一个名为 main-0.1 的目录。
该目录内含有五个档案,就是刚刚的程式加上一个 Makefile 的规则档案。你可以到该目录下去看看 Makefile 的内容,
在这一版当中含有 main 与 clean 两个标的功能而已。至于 0.2 版则加入了 install 与 uninstall 的规则设定。
接下来,请看一下我们的作法啰:
与之前的结果非常类似,只是鸟哥将 Makefile 直接给您了!但如果你下达 make install 时,系统会告知没有 install 的 target
啊!而且版本是 0.1 也告知了。那么如何更新到 0.2 版呢?透过这个 patch 档案吧!这个档案的内容有点像这样:
上面表格内有个底线的部分,那代表使用 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’,也就是说,
-pxx 那个 xx 代表‘拿掉几个斜线(/)’的意思!这样可以理解了吗?
好了,根据刚刚上头的资料,我们可以发现比较的档案是在 main-0.1/xxx 与 main-0.2/xxx ,
所以说,如果你是在 main-0.1 底下,并且想要处理更新时,就得要拿掉一个目录 (因为并没有 main-0.2 的目录存在,
我们是在当前的目录进行更新的!),因此使用的是 -p1 才对喔!所以:
很有趣的练习吧!所以你只要下载 patch file 就能够对你的软体原始码更新了!只不过更新了原始码并非软体就更新!你还是得要将该软体进行编译后,才会是最终正确的软体喔! 因为 patch 的功能主要仅只是更新原始码档案而已!切记切记!此外,如果你 patch 错误呢?没关系的!我们的 patch 是可以还原的啊!透过‘ patch -R < ../main_0.1_to_0.2.patch ’就可以还原啦!很有趣吧!
函式库管理 在我们的 Linux 作业系统当中,函式库是很重要的一个项目。
因为很多的软体之间都会互相取用彼此提供的函式库来进行特殊功能的运作,
例如很多需要验证身份的程式都习惯利用 PAM 这个模组提供的验证机制来实作,而很多网路连线机制则习惯利用 SSL
函式库来进行连线加密的机制。所以说,函式库的利用是很重要的。不过,
函式库又依照是否被编译到程式内部而分为动态与静态函式库,这两者之间有何差异?哪一种函式库比较好?
底下我们就来谈一谈先! 动态与静态函式库 首先我们要知道的是,函式库的类型有哪些?依据函式库被使用的类型而分为两大类,分别是静态
(Static) 与动态 (Dynamic) 函式库两类。底下我们来谈一谈这两种类行的函式库吧!
目前的 Linux distribution 比较倾向于使用动态函式库,因为如同上面提到的最重要的一点, 就是函式库的升级方便!由于 Linux 系统里面的软体相依性太复杂了,如果使用太多的静态函式库,那么升级某一个函式库时, 都会对整个系统造成很大的冲击!因为其他相依的执行档也要同时重新编译啊! 这个时候动态函式库可就有用多了,因为只要动态函式库升级就好,其他的软体根本无须变动。 那么这些函式库放置在哪里呢?绝大多数的函式库都放置在:/usr/lib, /lib 目录下! 此外,Linux 系统里面很多的函式库其实 kernel 就提供了,那么 kernel 的函式库放在哪里?呵呵!就是在 /lib/modules 里面啦!里面的资料可多着呢!不过要注意的是, 不同版本的核心提供的函式库差异性是挺大的,所以 kernel 2.4.xx 版本的系统不要想将核心换成 2.6.xx 喔! 很容易由于函式库的不同而导致很多原本可以执行的软体无法顺利运作呢! ldconfig 与 /etc/ld.so.conf 在了解了动态与静态函式库,也知道我们目前的 Linux 大多是将函式库做成动态函式库之后,再来要知道的就是,那有没有办法增加函式库的读取效能? 我们知道记忆体的存取速度是硬碟的好几倍,所以,如果我们将常用到的动态函式库先载入记忆体当中 (快取, cache),如此一来,当软体要取用动态函式库时,就不需要从头由硬碟里面读出啰! 这样不就可以增进动态函式库的读取速度?没错,是这样的!这个时候就需要 ldconfig 与 /etc/ld.so.conf 的协助了。 如何将动态函式库载入快取记忆体当中呢?
图 5.2.1、使用 ldconfig 预载入动态函式库到记忆体中 事实上, ldconfig 还可以用来判断动态函式库的连结资讯呢!赶紧利用 CentOS 来测试看看。假设你想要将目前你系统下的 MySQL 函式库加入到快取当中时,可以这样做:
透过上面的动作,我们可以将 MySQL 的相关函式库给他读入快取当中,这样可以加快函式库读取的效率呢! 在某些时候,你可能会自行加入某些 Tarball 安装的动态函式库,而你想要让这些动态函式库的相关连结可以被读入到快取当中, 这个时候你可以将动态函式库所在的目录名称写入 /etc/ld.so.conf 当中,然后执行 ldconfig 就可以啦! 程式的动态函式库解析: ldd 说了这么多,那么我如何判断某个可执行的 binary 档案含有什么动态函式库呢?很简单,利用 ldd 就可以晓得了!例如我想要知道 /usr/bin/passwd 这个程式含有的动态函式库有哪些,可以这样做:
未来如果你常常升级安装 RPM 的软体时 (下一章节会介绍),应该常常会发现那个‘ 相依属性’的问题吧!没错!我们可以先以 ldd 来视察‘相依函式库’之间的相关性!以先取得了解! 例如上面的例子中,我们检查了 libc.so.6 这个在 /lib 当中的函式库,结果发现他其实还跟 ld-linux.so.2 有关!所以我们就需要来了解一下,那个档案到底是什么软体的函式库呀?使用 -v 这个参数还可以得知该函式库来自于哪一个软体!像上面的资料中,就可以得到该 libc.so.6 其实可以支援 GLIBC_2.1 等的版本! 检验软体正确性 前面提到很多升级与安装需要注意的事项,因为我们需要克服很多的程式漏洞,所以需要前往 Linux distribution 或者是某些软体开发商的网站,下载最新并且较安全的软体档案来安装才行。 好了,那么‘有没有可能我们下载的档案本身就有问题?’ 是可能的!因为 cracker 无所不在,很多的软体开发商已经公布过他们的网页所放置的档案曾经被窜改过! 那怎么办?连下载原版的资料都可能有问题了?难道没有办法判断档案的正确性吗? 这个时候我们就要透过每个档案独特的指纹验证资料了!因为每个档案的内容与档案大小都不相同, 所以如果一个档案被修改之后,必然会有部分的资讯不一样!利用这个咚咚,我们可以使用 MD5 这个指纹验证机制来判断该档案有没有被更动过! 举个例子来说,台湾高速网路中心所提供的 CentOS 5.3 原版光碟下载点: http://ftp.twaren.net/Linux/CentOS/5.3/isos/i386/ 同时提供了 CentOS 5.3 所有光碟/DVD 的 ISO 档案 MD5 编码,透过这个编码的比对,
我们就可以晓得下载的档案是否有问题。那么万一 CentOS 提供的光碟映象档被下载之后,让有心人士偷偷修改过,再转到
Internet 上面流传,那么你下载的这个档案偏偏不是原厂提供的,呵呵!
你能保证该档案的内容完全没有问题吗?当然不能对不对!是的,这个时候就有
md5sum 与 sha1sum 这个档案指纹的咚咚出现啦!说说他的用法吧! md5sum / sha1sum 目前有多种机制可以计算档案的指纹码,我们选择使用较为广泛的 MD5 与 SHA1 加密机制来处理。 同样的,我们以高速电脑中心谈到的 CentOS 5.3 的网路安装映像档来处理试看看好了。 在上面的连结网址上面,你会看到几个档案:
如果你下载了 CentOS-5.3-i386-netinstall.iso 后,再以 md5sum 与 sha1sum 去检验这个档案时, 档案所回传的指纹码应该要与网站上面提供的档案指纹码相同才对!我们由网站上面提供的指纹码知道这个映像档的指纹为:
一般而言,每个系统里面的档案内容大概都不相同,例如你的系统中的 /etc/passwd 这个登入资讯档与我的一定不一样,因为我们的使用者与密码、 Shell 及家目录等大概都不相同,所以由 md5sum 这个档案指纹分析程式所自行计算出来的指纹表当然就不相同啰! 好了,那么如何应用这个东西呢?基本上,你必须要在你的 Linux 系统上为你的这些重要的档案进行指纹资料库的建立 (好像在做户口调查!),将底下这些档案建立资料库:
这几个档案最容易被修改了!因为很多木马程式执行的时候,还是会有所谓的‘执行序, PID’为了怕被 root 追查出来,所以他们都会修改这些检查排程的档案,如果你可以替这些档案建立指纹资料库 (就是使用 md5sum 检查一次,将该档案指纹记录下来,然后常常以 shell script 的方式由程式自行来检查指纹表是否不同了!),那么对于档案系统会比较安全啦! 重点回顾
本章习题
实作题部分:
情境模拟题部分:
参考资料与延伸阅读
2002/08/21:第一次完成 2003/02/11:重新编排与加入 FAQ 2004/03/25:原本是 Tarball 与 RPM ,本日开始将 Tarball 与 RPM 分开说明与讲解(后续会花好几天喔!), 最重要的是 Source code 的说明,并提到相关的 gcc compile 功能等等! 2004/04/10:经历了当兵中的无奈生活,终于将这篇给他完工了~(当时的鸟哥在将军渔港与青山港~) 2005/09/30:旧版文章 (Tarball 与 RPM 的简单说明) 移动到 此处 。 2005/10/01:将风格作个转变之外,也将一些测试移转到 FC4 上面进行! 2008/01/10:感谢网友 ayttk 的说明,原本的 make 语法网页已经移动到其他地方了,请参考 这里。 2009/06/04:将基于 FC4 撰写的文章移动到 此处 2009/06/20:增加一个小练习,需要使用到 X software development 的软体群组喔! 2009/09/15:加入一个情境模拟,其实有点功力练功练功而已的习题啰! |
|||||||||||||||||||||||||||||||||||||||||||||