Linux 上面的软件几乎都是经过 GPL 的授权,所以每个软件几乎均提供原始程序码, 并且你可以自行修改该程序码,以符合你个人的需求。

一、可运行档

在 Linux 系统上面,一个文件能不能被运行看的是有没有可运行的那个权限 (具有 x permission),不过,Linux 系统上真正认识的可运行档其实是二进位文件 ( binary program)。

# 先以系统的文件测试看看:
[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

如果是 binary 而且是可以运行的时候,他就会显示运行档类别 (ELF 32-bit LSB executable), 同时会说明是否使用动态函式库 (shared libs),而如果是一般的 script ,那他就会显示出 text executables 之类的字样。

二、什么是函式库

我们在编写程序的时候,可能引用现有的库来实现部分功能,而不需要所有功能都自己实现,比如认证模块,我们可以引用PAM提供的功能!

函式库又分为动态与静态函式库,我们后面再讨论~

三、什么是 make 与 configure

我们使用编译器编译的时候,需要一系列编译、链接操作,如果工程比较大,一个个操作起来特别麻烦,这时候就需要通过make 这个命令的相关功能来进行编译过程的命令简化~

当运行 make 时,make 会在当时的目录下搜寻 Makefile (or makefile) 这个文字档,而 Makefile 里面则记录了原始码如何编译的详细资讯! make 会自动的判别原始码是否经过变动了,而自动升级运行档,是软件工程师相当好用的一个辅助工具。

通常软件开发商都会写一支侦测程序来侦测使用者的作业环境, 以及该作业环境是否有软件开发商所需要的其他功能,该侦测程序侦测完毕后,就会主动的创建这个 Makefile 的守则文件。通常这支侦测程序的档名为 configure 或者是 config 。

一般来说,侦测程序会侦测的数据大约有底下这些:

是否有适合的编译器可以编译本软件的程序码;
是否已经存在本软件所需要的函式库,或其他需要的相依软件;
操作系统平台是否适合本软件,包括 Linux 的核心版本;
核心的表头定义档 (header include) 是否存在 (驱动程序必须要的侦测)。

因此,我们拿到软件的源码之后,要进行的任务其实只有两个,一个是运行 configure 来创建 Makefile , 这个步骤一定要成功!成功之后再以 make 来呼叫所需要的数据来编译即可。

四、什么是 Tarball 的软件

所谓的 Tarball 文件,其实就是将软件的所有原始码文件先以 tar 打包,然后再以压缩技术来压缩,通常最常见的就是以 gzip 来压缩了。将他解压缩之后,里面的文件通常就会有:

原始程序码文件;
侦测程序文件 (可能是 configure 或 config 等档名);
本软件的简易说明与安装说明 (INSTALL 或 README)。

五、如何安装与升级软件

基本上升级的方法可以分为两大类:

1.直接以原始码透过编译来安装与升级;
2.直接以编译好的 binary program 来安装与升级。

大部分的 tarball 软件之安装的命令下达方式:

  1. ./configure
    这个步骤就是在创建 Makefile 这个文件。通常程序开发者会写一支 scripts 来检查你的 Linux 系统、相关的软件属性等等,这个步骤相当的重要, 因为未来你的安装资讯都是这一步骤内完成的!另外,这个步骤的相关资讯应该要参考一下该目录下的 README 或 INSTALL 相关的文件!
  2. make clean
    make 会读取 Makefile 中关於 clean 的工作。这个步骤不一定会有,但是希望运行一下,因为他可以去除目标文件!因为谁也不确定原始码里面到底有没有包含上次编译过的目标文件 (*.o) 存在,所以当然还是清除一下比较妥当的。 至少等一下新编译出来的运行档我们可以确定是使用自己的机器所编译完成的嘛!
  3. make
    make 会依据 Makefile 当中的默认工作进行编译的行为!编译的工作主要是进行 gcc 来将原始码编译成为可以被运行的 object files ,但是这些 object files 通常还需要一些函式库之类的 link 后,才能产生一个完整的运行档!使用 make 就是要将原始码编译成为可以被运行的可运行档,而这个可运行档会放置在目前所在的目录之下, 尚未被安装到预定安装的目录中;
  4. make install
    通常这就是最后的安装步骤了,make 会依据 Makefile 这个文件里面关於 install 的项目,将上一个步骤所编译完成的数据给他安装到预定的目录中,就完成安装啦!

基本上,在默认的情况下,原本的 Linux distribution 释出安装的软件大多是在 /usr 里面的,而使用者自行安装的软件则建议放置在 /usr/local 里面(原始码则建议放置在 /usr/local/src (src 为 source 的缩写)底下)。

如果单一软件的文件都在同一个目录之下,那么要移除该软件只要将该目录移除即可视为该软件已经被移除。

为安装到单独目录的软件之 man page 加入 man path 搜寻:
如果你安装的软件放置到 /usr/local/software/ ,那么 man page 搜寻的配置中,可能就得要在 /etc/man.config 内的 40~50 行左右处,写入如下的一行:

MANPATH /usr/local/software/man

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

利用 patch 升级原始码

patch 的基本语法如下:

patch -p数字 < patch_file

patch 的主要功能在升级原始码,所以升级原始码之后,还需要进行重新编译的动作才行~

六、动态与静态函式库

1.静态函式库的特色:

扩展名:(扩展名为 .a)
这类的函式库通常扩展名为 libxxx.a 的类型;

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

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

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

2.动态函式库的特色:

扩展名:(扩展名为 .so)
这类函式库通常扩展名为 libxxx.so 的类型;

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

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

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

linux绝大多数的函式库都放置在:/usr/lib, /lib 目录下,还有核心自带的函式库则放在 /lib/modules 里面。

七、ldconfig 与 /etc/ld.so.conf

如果我们将常用到的动态函式库先加载内存当中 (缓存, cache),当软件要取用动态函式库时,就不需要从头由硬盘里面读出,从而增进动态函式库的读取速度。

那么,如何将动态函式库加载高速缓存当中呢?

首先,我们必须要在 /etc/ld.so.conf 里面写下『 想要读入高速缓存当中的动态函式库所在的目录』,注意, 是目录而不是文件;
接下来则是利用 ldconfig 这个运行档将 /etc/ld.so.conf 的数据读入缓存当中;
同时也将数据记录一份在 /etc/ld.so.cache 这个文件当中!

八、程序的动态函式库解析: ldd

ldd指令可用来判断某个可运行的 binary 文件含有什么动态函式库。

[root@www ~]# ldd [-vdr] [filename]
选项与参数:
-v :列出所有内容资讯;
-d :重新将数据有遗失的 link 点秀出来!
-r :将 ELF 有关的错误内容秀出来!

九、检验软件的正确性:md5sum / sha1sum

我们要如何检验下载的软件的可靠性,确保软件没被第三方修改过?

目前有多种机制可以计算文件的指纹码,我们选择使用较为广泛的 MD5 与 SHA1 加密机制来处理。

[root@www ~]# md5sum/sha1sum [-bct] filename
[root@www ~]# md5sum/sha1sum [--status|--warn] --check filename
选项与参数:
-b :使用 binary 的读档方式,默认为 Windows/DOS 文件型态的读取方式;
-c :检验文件指纹;
-t :以文字型态来读取文件指纹。

下载完一个软件后,我们可以用md5sum / sha1sum查看MD5码和SHA1码,然后与官网提供的码对比,如果一样,说明没有修改过。

那么如何应用这个东西呢?基本上,你必须要在你的 Linux 系统上为你的这些重要的文件进行指纹数据库的创建,将底下这些文件创建数据库:

/etc/passwd
/etc/shadow( 假如你不让使用者改口令了 )
/etc/group
/usr/bin/passwd
/sbin/portmap
/bin/login ( 这个也很容易被骇! )
/bin/ls
/bin/ps
/usr/bin/top

如果你可以替这些文件创建指纹数据库 (就是使用 md5sum 检查一次,将该文件指纹记录下来,然后常常以 shell script 的方式由程序自行来检查指纹表是否不同了!),那么对於文件系统会比较安全。

十、编译的简单范例

1.范例1

编辑源代码:

[root@www ~]# vim hello.c
#include <stdio.h>
int main(void)
{
    printf("Hello World\n");
}

编译与测试运行:
方式1:

[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   <==没有加上任何参数,则运行档的档名会被自动配置为 a.out
-rw-r--r-- 1 root root   72 Jun  5 02:40 hello.c

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

方式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  <==这就是可运行档! -o 的结果
-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

2.范例2

主、副程序连结:副程序的编译:

# 1. 编辑主程序:
[root@www ~]# vim thanks.c
#include <stdio.h>
int main(void)
{
    printf("Hello World\n");
    thanks_2();
}

[root@www ~]# vim thanks_2.c
#include <stdio.h>
void thanks_2(void)
{
    printf("Thank you!\n");
}

# 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 ,然后再以连结制作出新的 binary 可运行档即可!而不必重新编译其他没有更动过的原始码文件。

3.范例3

呼叫外部函式库:加入连结的函式库

[root@www ~]# vim sin.c
#include <stdio.h>
int main(void)
{
    float value;
    value = sin ( 3.14 / 2 );
    printf("%f\n",value);
}

编译时加入额外函式库连结的方式:

[root@www ~]# gcc sin.c -lm -L/lib -L/usr/lib  <==重点在 -lm 
[root@www ~]# ./a.out                          <==尝试运行新文件!
1.000000

-l :是『加入某个函式库(library)』的意思,
m :则是 libm.so 这个函式库,其中, lib 与扩展名(.a 或 .so)不需要写。

上例的意思是:我要的函式库 libm.so 请到 /lib 或 /usr/lib 里面搜寻

附录:

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

# 仅将原始码编译成为目标档,并不制作连结等功能:
[root@www ~]# gcc -c hello.c
# 会自动的产生 hello.o 这个文件,但是并不会产生 binary 运行档。

# 在编译的时候,依据作业环境给予最佳化运行速度
[root@www ~]# gcc -O hello.c -c
# 会自动的产生 hello.o 这个文件,并且进行最佳化

# 在进行 binary file 制作时,将连结的函式库与相关的路径填入
[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 之后,程序的编译会变的较为严谨一点,
# 所以警告信息也会显示出来!