Linux 软件安装与管理
编译安装
编译程序是将源代码编译成机器语言生成可执行二进制程序的过程。
在 Linux 中,可以使用 configure
命令对系统环境进行检测和收集,生成 Makefile
文件,make
命令根据 Makefile
文件的配置来对源文件进行编译,生成可执行二进制程序。
简单来说,通过源代码安装程序的步骤如下:
- 从网上下载包含源代码的压缩包;
- 解压压缩包;
- 使用
gcc
进行源代码编译,生成目标文件(object files); - 使用
gcc
进行函数库、主程序和辅助程序的链接,形成二进制文件; - 将二进制文件及相关配置文件安装到指定目录。
编译安装需要用到的程序包括:gcc
、make
、autoconfig
,以及相关的库文件和头文件。通常可以使用 yum groupinstall "Development Tools"
命令来安装所需套件。
单一程序
编写一个简单的 C 语言源代码进行编译测试:
[root@101c7 ~]$ vi hello.c #include <stdio.h> int main (void) { printf("hello\n"); } "hello.c" [New] 5L, 58C written
使用 gcc 进行编译并运行:
[root@101c7 ~]$ gcc hello.c [root@101c7 ~]$ ll total 20 -rw-------. 1 root root 1260 Sep 8 01:38 anaconda-ks.cfg -rwxr-xr-x. 1 root root 8360 Sep 21 20:33 a.out -rw-r--r--. 1 root root 59 Sep 21 20:32 hello.c [root@101c7 ~]$ ./a.out hello
若没有指定输出文件名,则可执行文件名为 a.out。使用 -o
参数指定输出文件名为 hello.exe:
[root@101c7 ~]$ gcc -c hello.c [root@101c7 ~]$ gcc -o hello.exe hello.o [root@101c7 ~]$ ./hello.exe hello
主副程序链接
如果在主程序里又调用了另一个副程序,例如新建一个 world.c
,再到 hello.c
调用它:
[root@234c8 ~]$ vi world.c #include <stdio.h> void world (void) { printf("world!\n"); } "world.c" [New] 5L, 62C written [root@234c8 ~]$ vi hello.c #include <stdio.h> int main (void) { printf("hello "); world (); } "hello.c" 6L, 69C written
同样执行编译,最终生成二进制文件 helloworld
:
[root@234c8 ~]$ gcc -c hello.c world.c [root@234c8 ~]$ ll total 20 -rw-------. 1 root root 1260 Sep 8 01:38 anaconda-ks.cfg -rw-r--r--. 1 root root 69 Sep 21 20:43 hello.c -rw-r--r--. 1 root root 1560 Sep 21 20:44 hello.o -rw-r--r--. 1 root root 62 Sep 21 20:42 world.c -rw-r--r--. 1 root root 1488 Sep 21 20:44 world.o [root@234c8 ~]$ gcc -o helloworld hello.o world.o [root@234c8 ~]$ ./helloworld hello world!
这个生成的二进制文件 helloworld
包含了两个源代码里的内容。如果修改了源文件 world.c
内容,只需要重新编译 world.c
文件,将新的 world.o
和 hello.o
链接制作出修改过后的二进制可执行文件。
调用外部函数库
如果调用的是系统函数库,直接在源文件里 include
进来。也可以使用 -I
参数来指定 include
文件位置:
[root@234c8 ~]$ vi sin.c #include <stdio.h> #include <math.h> int main (void) { float value; value = sin (3/2); printf ("%f\n",value); } "sin.c" 8L, 115C written [root@234c8 ~]$ gcc sin.c -I/usr/include [root@234c8 ~]$ ./a.out 0.841471
如果要指定函数库位置可以使用 -L
参数。另外,-
参数表示加入某函数库,m
代表 libm.so
函数库:
[root@234c8 ~]$ gcc sin.c -lm -L/lib -L/lib64
创建 Makefile
Makefile
文件作用为简化整个编译流程。可以手动创建一个 Makefile
来测试下:
[root@234c8 ~]$ vi makefile main: hello.o world.o gcc -o main hello.o world.o "makefile" [New] 2L, 51C written [root@234c8 ~]$ make cc -c -o hello.o hello.c cc -c -o world.o world.c gcc -o main hello.o world.o [root@234c8 ~]$ ll total 40 -rw-r--r--. 1 root root 69 Sep 21 20:43 hello.c -rw-r--r--. 1 root root 1560 Sep 21 21:05 hello.o -rwxr-xr-x. 1 root root 8472 Sep 21 21:05 main -rw-r--r--. 1 root root 51 Sep 21 21:05 makefile -rw-r--r--. 1 root root 62 Sep 21 20:42 world.c -rw-r--r--. 1 root root 1488 Sep 21 21:05 world.o [root@234c8 ~]$ make make: `main' is up to date. [root@234c8 ~]$ ./main hello world!
目标(target
)与相关文件之间以分号:
隔开,gcc
行前必须使用 [Tab]
按键来缩进。
如果更新了源文件,只需要再次执行 make
命令就可以将生成的可执行文件更新。
当有两个以上执行动作时,例如编译完成后删除生成的 .o
文件,可以直接在后面接自定义阶段名:
[root@234c8 ~]$ vi makefile main: hello.o world.o gcc -o main hello.o world.o clean: rm -f hello.o world.o "makefile" 4L, 81C written [root@234c8 ~]$ make clean rm -f hello.o world.o
运行时,在 make
后面接阶段名即可执行那一阶段定义的命令。
默认不接参数时执行 main
中命令,想要先执行 main
段再执行 clean
段,可以把两个阶段名都写出来:
[root@234c8 ~]$ make main clean cc -c -o hello.o hello.c cc -c -o world.o world.c gcc -o main hello.o world.o rm -f hello.o world.o
在 Makefile
中可以使用变量来简化内容,例如将 hello.o
world.o
定义为 OBJS
:
[root@234c8 ~]$ vi makefile OBJS = hello.o world.o main: ${OBJS} gcc -o main ${OBJS} clean: rm -f ${OBJS}
另外可以用 $@
代表目前的 target
(也就是 main
)。
从源代码安装程序
通常建议将源代码放在 /usr/local/src
目录下,软件安装到 /usr/local
目录下。
一般从网上下载的源码包安装实际操作步骤如下:
- 取得原始文件,一般是
tar
压缩包; - 将
tar
解压缩,读取里面的INSTALL
或README
等文档; - 根据文档的要求安装好一些依赖软件;
- 创建
makefile
,一般用./configure
脚本来检测系统与相关软件属性; - 运行
make clean; make
以makefile
作为配置来编译; - 运行
make install
将目标文件安装到指定路径。
以上每个步骤都必须执行成功,才能进行下一步骤。
下面以安装 NTP 举例。先到官网 http://www.ntp.org/downloads.html 找到最新的安装包用 wget
下载:
[root@234c8 ~]$ cd /usr/local/src [root@234c8 src]$ wget http://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.8p15.tar.gz 100%[=================================================================>] 7,015,970 23.5KB/s in 6m 2s 2021-09-21 22:14:23 (18.9 KB/s) - ‘ntp-4.2.8p15.tar.gz’ saved [7015970/7015970]
验证下载的 tar
文件 MD5 值是否正确:
[root@234c8 src]$ wget http://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.8p15.tar.gz.md5 100%[=================================================================>] 61 --.-K/s in 0s 2021-09-21 23:01:20 (9.29 MB/s) - ‘ntp-4.2.8p15.tar.gz.md5’ saved [61/61] [root@234c8 src]$ md5sum ntp-4.2.8p15.tar.gz --check ntp-4.2.8p15.tar.gz.md5 md5sum: ntp-4.2.8p15.tar.gz: no properly formatted MD5 checksum lines found ntp-4.2.8p15.tar.gz: OK
将其解压并阅读 README
文件:
[root@234c8 src]$ tar -zxv -f ntp-4.2.8p15.tar.gz ntp-4.2.8p15/lib/isc/ia64/include/isc/ ntp-4.2.8p15/lib/isc/ia64/include/isc/atomic.h ntp-4.2.8p15/lib/isc/alpha/include/ ntp-4.2.8p15/lib/isc/alpha/include/isc/ ntp-4.2.8p15/lib/isc/alpha/include/isc/atomic.h [root@234c8 src]$ cd ntp-4.2.8p15 ; less README Submit patches, bug reports, and enhancement requests via http://bugs.ntp.org
README 文件解释了每个文件的作用。然后使用./configure
创建 makefile,并加入 --prefix
参数指定安装位置:
[root@234c8 ntp-4.2.8p15]$ ./configure --help [root@234c8 ntp-4.2.8p15]$ ./configure --prefix=/usr/local/ntp --enable-all-clocks --enable-parse-clocks config.status: creating Makefile config.status: creating config.h config.status: creating evconfig-private.h config.status: evconfig-private.h is unchanged config.status: executing depfiles commands config.status: executing libtool commands [root@234c8 ntp-4.2.8p15]$ cat Makefile | grep /usr/local/ntp NTP_KEYSDIR = /usr/local/ntp/etc PERLLIBDIR = /usr/local/ntp/share/ntp/lib prefix = /usr/local/ntp
最后使用 make
来安装:
[root@234c8 ntp-4.2.8p15]$ make clean; make make[2]: Entering directory `/usr/local/src/ntp-4.2.8p15' make[2]: Leaving directory `/usr/local/src/ntp-4.2.8p15' make[1]: Leaving directory `/usr/local/src/ntp-4.2.8p15' [root@234c8 ntp-4.2.8p15]$ make check make[2]: Leaving directory `/usr/local/src/ntp-4.2.8p15' make[1]: Leaving directory `/usr/local/src/ntp-4.2.8p15' [root@234c8 ntp-4.2.8p15]$ make install Installing stand-alone HTML documentation make[3]: Leaving directory `/usr/local/src/ntp-4.2.8p15' make[2]: Leaving directory `/usr/local/src/ntp-4.2.8p15' make[1]: Leaving directory `/usr/local/src/ntp-4.2.8p15' [root@234c8 ntp-4.2.8p15]$ ll /usr/local/ntp/ total 0 drwxr-xr-x. 2 root root 189 Sep 21 22:26 bin drwxr-xr-x. 2 root root 6 Sep 21 22:26 libexec drwxr-xr-x. 2 root root 6 Sep 21 22:26 sbin drwxr-xr-x. 5 root root 39 Sep 21 22:26 share
使用 patch
命令来对源码进行更新后,还需要再次编译才能生效。
函数库管理
在 Linux 下依据函数库是否被编译到程序内部分为动态与静态函数库:
-
静态(Static)
一般扩展名为
.a
,在编译时直接整合到执行程序中。 -
动态(Dynamic)
扩展名为
.so
,在编译时程序只有一个指向(Pointer)位置,需要用到时才会去读取。
现在软件偏向使用动态函数库,这样方便升级函数库后不需重新编译程序。
将函数库常驻内存
可以将常用的动态函数库载入内存中,这样程序调用函数库时比从硬盘读取速度快。方法如下:
- 在
/etc/ld.so.conf
里写明要读入内存中的函数库目录; - 利用
ldconfig
将ld.so.conf
中配置的函数库读入内存; - 同时也将数据记录一份在
/etc/ld.so.cache
这个文件中。
假设要把 MySQL 数据库的函数库载入内存(实际上系统默认已经这么做了):
[root@234c8 ~]$ vi /etc/ld.so.conf.d/mariadb-x86_64.conf /usr/lib64/mysql [root@234c8 ~]$ ldconfig [root@234c8 ~]$ ldconfig -p | grep mysql libmysqlclient.so.18 (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient.so.18
程序的动态函数库解析
可以使用 ldd
命令来查询,例如查询 df
命令使用的动态函数库:
[root@234c8 ~]$ ldd /usr/bin/df linux-vdso.so.1 => (0x00007ffe7de99000) libc.so.6 => /lib64/libc.so.6 (0x00007fec1f049000) /lib64/ld-linux-x86-64.so.2 (0x00007fec1f417000)
也可以继续拿 ldd
来查询某个函数的相关函数:
[root@234c8 ~]$ ldd -v /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2 (0x00007f894f632000) linux-vdso.so.1 => (0x00007ffecdfd8000) Version information: /lib64/libc.so.6: ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2 ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2
这个命令可以用来检查程序安装时的依赖性。
软件管理
通常不同的分发商会将一些软件编译好成二进制,并通过软件管理平台提供给用户。Fedora/CentOS 系列使用 RPM 软件管理机制和 yum 线上更新模式,而 Debian/Ubuntu 使用 dpkg 软件管理机制和 APT 线上更新模式。
RPM 与 SRPM
RPM 的全名是 RedHat Package Manager,它要求软件文件安装环境与打包时一致,并且需要满足软件依赖性需求。
SRPM 是 Source RPM,也就是提供的 RPM 包的源文件,通常扩展名为 *.src.rpm。与普通源代码安装不同的是,要将它以 RPM 管理的方式编译成 RPM 文件,再安装到系统中。
RPM 包文件名通常有固定格式,例如:cronolog-1.6.2-14.el7.x86_64.rpm
:
cronolog
:软件名1.6.2
:软件版本。主版本为 1,在主版本架构下的功能更新变动次版本号 6,小修补则用动用再次版本号 2 来表示。14
:释出版本次数,也就是编译次数。可能由于一些 bug 或安全原因,重新调整过编译参数。el7.x86_64
:适合的硬件平台。如果没有硬件等级限制,用noarch
表示。rpm
:固定扩展名
RPM 的特点:
- RPM 内含已经编译过的程序与配置文件,不需要使用者重新编译。
- RPM 在被安装之前,会检查系统版本、剩余容量等,可避免被错误安装。
- RPM 文件本身提供软件版本信息、软件依赖信息、用途说明、文件内容等信息。
- RPM 管理的方式使用数据库记录 RPM 文件相关参数,方便升级、卸载、查询等。
RPM 使用 YUM 服务器提供下载,每次安装软件前,yum 都会先在线更新本地软件数据库,得出需要安装的软件列表。再到 YUM 服务器去获取软件,通过 RPM 的机制开始安装。使用 RPM 安装软件的信息会记录在 /var/lib/rpm/
目录下的数据库内。
SRPM 文件编译打包使用 rpmbuild --rebuild
命令。配置文件一般是 SPECS 目录下的 .spec
文件,修改完成后可以用 rpmbuild -bb spec
文件来编译成 RPM 文件。
RPM 命令
安装使用 -ivh
参数,比如安装 cronolog
软件:
[root@234c8 ~]$ rpm -ivh cronolog-1.6.2-14.el7.x86_64.rpm Preparing... ################################# [100%] Updating / installing... 1:cronolog-1.6.2-14.el7 ################################# [100%]
参数后面可以接多个 rpm 包来一次安装,也可以指定网络上的地址来安装。
其他一些有用的参数如下:
参数 | 说明 |
---|---|
--nodeps |
强制忽略软件依赖性来安装 |
--replacefiles |
直接覆盖掉原先安装过的软件 |
--replacepkgs |
直接重装 rpm 包 |
--force |
等于 --replacefiles 加 --replacepkgs |
--test |
测试 rpm 包是否能正确安装 |
--justdb |
在数据库有错误时,更新此软件在数据库内的信息 |
--nosignature |
忽略数字签名检查 |
--prefix 路径 |
指定软件安装到自定义目录 |
--noscripts |
禁止软件在安装时执行的命令 |
如果要升级软件使用 -Uvh
参数:
[root@234c8 ~]$ rpm -Uvh cronolog-1.6.2-14.el7.x86_64.rpm Preparing... ################################# [100%] package cronolog-1.6.2-14.el7.x86_64 is already installed
查询本机已安装的软件或 RPM 包可以使用以下命令及参数:
参数 | 说明 |
---|---|
-q |
仅查询软件是否有安装 |
-qa |
列出所有已经安装的软件 |
-qi |
列出软件详细信息 |
-ql |
列出软件的所有文件与目录路径 |
-qc |
列出软件的所有配置文件 |
-qd |
列出软件的说明文档 |
-qR |
列出软件的依赖情况 |
-qf |
找出查询文件属于哪一个已经安装的软件 |
-q --script |
列出是否含有安装后需要执行的脚本 |
-qp[icdlR] |
查询 RPM 文件内的信息 |
例如,查询 cronolog 软件安装的文件:
[root@234c8 ~]$ rpm -ql cronolog /usr/bin/cronosplit /usr/sbin/cronolog /usr/share/doc/cronolog-1.6.2
查询 cronolog 软件相关说明:
[root@234c8 ~]$ rpm -qi cronolog Name : cronolog Version : 1.6.2 Release : 14.el7 Architecture: x86_64
查询文件 /bin/rpcgen
属于哪个软件包。如果文件被误删了也可以查询:
[root@234c8 ~]$ rpm -qf /bin/rpcgen glibc-common-2.17-324.el7_9.x86_64
可以使用 -V
参数来验证某个软件是否缺失或更改了文件,例如查询 rootfiles:
[root@234c8 ~]$ rpm -V rootfiles
卸载软件使用 -e
参数,当软件没有依赖关系时才能卸载。如果强制移除软件造成数据库错误,可以使用 --rebuilddb
来重建数据库:
[root@234c8 ~]$ rpm --rebuilddb
YUM 命令
yum
(Yellowdog Updater Modified) 是一种通过线上服务器安装软件的工具,常用参数包括 -y
用于自动确认,以及 --installroot
用于指定安装路径。
使用 list
选项查询软件列表,使用 search
选项在线上搜索软件,例如搜索和 vim
有关的软件:
[root@234c8 ~]$ yum search vim Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: mirrors.tuna.tsinghua.edu.cn * epel: mirrors.tuna.tsinghua.edu.cn * extras: mirrors.tuna.tsinghua.edu.cn * updates: mirrors.tuna.tsinghua.edu.cn ============================== N/S matched: vim ======================================== beakerlib-vim-syntax.noarch : Files for syntax highlighting BeakerLib tests in VIM editor boxes-vim.noarch : Vim plugin for boxes fluxbox-vim-syntax.noarch : Fluxbox syntax scripts for vim geany-plugins-vimode.x86_64 : Vim-mode plugin for Geany
使用 info
选项搜索软件信息,例如搜索 gdisk
软件的信息:
[root@234c8 ~]$ yum info gdisk Available Packages Name : gdisk Arch : x86_64 Version : 0.8.10 Release : 3.el7 Size : 190 k
列出可升级的软件使用 list updates
命令:
[root@234c8 ~]$ yum list updates Updated Packages epel-release.noarch 7-14 epel kernel.x86_64 3.10.0-1160.42.2.el7 update
安装软件使用 install
命令,升级使用 update
命令,例如安装 agrep
软件:
[root@234c8 ~]$ yum install -y agrep Installed: agrep.x86_64 0:0.8.0-18.20140228gitc2f5d13.el7 Dependency Installed: tre.x86_64 0:0.8.0-18.20140228gitc2f5d13.el7 tre-common.noarch 0:0.8.0-18.20140228gitc2f5d13.el7 Complete!
也可以只下载 RPM 包但不安装(软件已安装的情况下使用 reinstall
代替 install
)。例如下载 agrep
到 /root/dl/
目录:
[root@server1 ~]$ yum install -y agrep --downloadonly --downloaddir=/root/dl/ [root@server1 ~]$ ll /root/dl/ total 96 -rw-r--r--. 1 root root 19924 Nov 4 2016 agrep-0.8.0-18.20140228gitc2f5d13.el7.x86_64.rpm -rw-r--r--. 1 root root 40868 Nov 4 2016 tre-0.8.0-18.20140228gitc2f5d13.el7.x86_64.rpm -rw-r--r--. 1 root root 32920 Nov 4 2016 tre-common-0.8.0-18.20140228gitc2f5d13.el7.noarch.rpm
软件删除使用 remove
命令,例如移除掉 agrep
:
[root@234c8 ~]$ yum remove agrep Remove 1 Package Installed size: 22 k Is this ok [y/N]: y Downloading packages: Running transaction check Running transaction test Transaction test succeeded Running transaction Erasing : agrep-0.8.0-18.20140228gitc2f5d13.el7.x86_64 1/1 Verifying : agrep-0.8.0-18.20140228gitc2f5d13.el7.x86_64 1/1 Removed: agrep.x86_64 0:0.8.0-18.20140228gitc2f5d13.el7 Complete!
如果想要加入自定义仓库地址,可以修改 yum 配置文件 /etc/yum.repos.d/CentOS-Base.repo
:
[root@234c8 ~]$ cat /etc/yum.repos.d/CentOS-Base.repo [base] name=CentOS-$releasever - Base mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra #baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
其中以中括号代表软件库名称,可以自己取名,其他设置如下:
- name:自定义仓库名,不要重名即可;
- mirrorlist:列出这个软件仓库可用的映射站点,可以不需要;
- baseurl:后面接实际软件仓库网址;
- enable:设置是否要启用此仓库;
- gpgcheck:是否要检查数字签名;
- gpgkey:数字签名文件位置。
设置好以后可以使用 repolist all
来查询:
[root@234c8 ~]$ yum repolist all repo id repo name status C7.8.2003-updates/x86_64 CentOS-7.8.2003 - Updates disabled base/7/x86_64 CentOS-7 - Base enabled: 10,072
yum 支持软件群组功能,可以使用下面选项来操作:
- grouplist:列出所有可用的软件群组;
- groupinfo:查询软件群组内容;
- groupinstall:安装一组软件群组;
- groupremove:删除某软件群组。
软件群组内软件分主要和可选,使用 groupinstall
只会安装主要软件,可以修改 /etc/yum.conf
配置,在 distroverpkg
下面加入一行配置 group_package_types=default, mandatory, optional
来安装所有软件群组的软件。
另外,想要通过代理连接下载,可以在配置文件中新增一行 proxy=代理服务器地址
来启用。