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=代理服务器地址
来启用。