第一部分:程序编译过程
一、软件运行环境介绍
- 操作系统内核、库、程序都为二进制文件,应用程序能否跨平台运行主要看该程序依赖的调用的二进制库文件是否兼容。
- 开发视角API:Application Program Interface 应用程序接口,程序员面对编程接口。应用系统与操作系统间的接口
-
应用视角ABI:Application Binary Interface 应用二进制接口,程序应用者面对运行程序的接口,应用程序与调用库之间的接口。
Linux:ELF(Executable and Linkable Format)
Windows:PE(Portable Executable)
- 解决方案:统一应用程序开发时调用库接口标准,即:统一API的标准,POSIX标准:Portable Operating System 可移执操作系统。遵循该标准的应用程序代码只需要在相应用的操作系统平台下重新编译即可安装使用。该标准依然是API层面的兼容性解决方案。
二、软件编译过程
我们获取应用程序的源码包后不能直接运行在操作系统平台,需要通过编译成不同操作系统支持ABI的才有运行在不同的操作系统平台上
程序源代码 --> 预处理 --> 编译 --> 汇编 --> 链接
1.预处理: 根据程序源代码中的预处理指令格式化源代码并输出为.i结尾的文件为编译做准备
2.编译: 对预处理文件进行编译,生成了汇编文件,基于.i文件中的预编译指令生成.s文件,里面保存的是汇编代码
3.汇编: 对汇编文件进行编译,生成了目标文件,基于编译过程生成的.s文件里的汇编代码转换成二进制机器码,最后输出为.o文件
4.链接: 对目标文件进行链接,生成可执行文件,将汇编生成的二进制文件及程序调用的库文件链接打包生成执行文件,链接分为两种因此编译分为两种:一种是静态编译,一种是动态编译
• 静态编译:
将程序调用的第三方库文件一起找包生成可执行文件,特点是安装时将依赖的第三方库文件一起释放安装,解决目标操作系统平台因没有依赖的库文件而运行失败的问题。但是静态链接生成的可执行程序体积较大,如果程序更新则需要重新再次编译。静态库后缀为.a文件
• 动态编译:
只是对程序调用的第三方库文件做个链接,并不真正将依赖的库文件打包在可执行文件内,而是做一个动态链接。在目标系统程序运行时调用目标操作系统中的库就可以,但如果没有依赖的库的则会运行失败。动态库为.so文件
5.GCC编译过程
• 预处理:生成预编译文件(.文件):gcc –E hello.c –o hello.i
(对hello.c文件进行预处理,生成了hello.i 文件)
• 编译:生成汇编代码(.s文件):gcc –S hello.i –o hello.s
(对预处理文件进行编译,生成了汇编文件 )
• 汇编:生成目标文件(.o文件):gcc –c hello.s –o hello.o
(对汇编文件进行编译,生成了目标文件)
• 链接:生成可执行文件:gcc hello.o –o hello
(对目标文件进行链接,生成可执行文件)
三、Linux系统中应用程序介绍
系统内安装的程序分两部分存储信息,一部分为程序的元数据信息,另一部分有应用程序数据。
1.程序的元数据:
系统内安装的rpm程序包信息(名称,版本,依赖性,描述等 )都被注册在rpm数据库内,每次安装rpm包都会查询数据库程序是否已经存在,如果存在则rpm包管理器则会反馈不同的信息。该数据库位于/var/lib/rpm
Ø 程序包名称及版本
Ø 依赖关系
Ø 功能说明
Ø 包安装后生成的各文件路径及校验码信息
rpm 系统出了问题,不能安装和查询,可能会是数据库出现问题,可以使用下面命令重建
rpm --initdb:如果事先没有数据库,则会新建一个,如果已经存在数据库则不进行任何操作
rpm –rebuilddb:重新构建数据库,会覆盖原有数据库
2.应用程序数据:
应用程序数据才是我们真正使用的能够被向操作系统提请为进程的程序文件
3.库文件:
Linux下很多应用程序都是动态编译生成的,因此会很多程序都依赖系统提供的基本库文件,而且很多应用程序间也存在着依赖关系,为系统提供了高效规范的处理这些依赖关系将所有可用库文件名及文件路径 映射关系以模块化的配置文件形式存在指定位置,并向用户提供了应用程序与库文件之间依赖关系查询的命令接口,具体如下:
Ø 配置文件位置:/etc/ld.so.conf, /etc/ld.so.conf.d/*.conf
Ø 缓存文件位置:/etc/ld.so.cache
Ø ldd命令:查看二进制程序所依赖的库文件 例:ldd /usr/bin/cat
Ø 管理及查看本机装载的库文件
• ldconfig加载配置文件中指定的库文
• ldconfig -p显示本机已经缓存的所有可用库文件名及文件路径映射关系
4.rpm包介绍
1.rpm包名称
包名称-[功能备注]-主版本号.次版本号.修正号-编译次数-发行商定制的发行版本号-硬件架构平台.rpm
Ø 功能备注:tools工具包、devel开发包、libs库、utils功能包、
Ø 主版本号:功能模块或架构上有大的变更
Ø 次版本号:功能模块加强
Ø 修正号:bug修复
2. rpm包结构:
Ø RPM包内的程序文件
Ø RPM的元数据,如名称,版本,依赖性,描述等
Ø 安装或卸载时运行的脚本 (用于定制配置文件,如创建用户等)
第二部分:程序包管理
rpm程序包管理工具将编译好的应用程序的各组成文件打包一个或几个程序包文件,从而 方便快捷地实现程序包的安装、卸载、查询、升级和校验等管理操作
一.rpm选项介绍
rpm程序包管理工具的选项比较多,这里还是总结一下rpm的man手册内各选项之间的搭配关系。我们可以把rpm的选项分为三个级别来理解记忆,一级为包管理类选项命令行内必选选项,二级为可选的功能性微调选项,三级辅助通用选项:-v显示详细信息、-h显示处理进度
1.一级包管理类必选选项:
-i安装、-e卸载、-U -F升级、-V校验、-q查询
查询、校验操作都可以搭配[select-options]
包的安装、升级操作都可以搭配[install-options]
包卸载操作搭配的选项则比较混乱我们只需记住-e
2.二级功能性微调可选选项:
安装选项、查询选项、校验选项、选择选项,详细信息见文章最后的微调选项附录部分
3.一、二级选项搭配关系:
1.包查询操作:搭配选择项或查询选项
rpm {-q|--query} [select-options] [query-options]
2.包校验操作:搭配选择项或校验选项
rpm {-V|--verify} [select-options] [verify-options]
3.包安装操作:搭配安装选项
rpm {-i|--install} [install-options] PACKAGE_FILE …
4.包重新安装:搭配安装选项
rpm {--reinstall} [install-options] PACKAGE_FILE ...
5.包升级操作:搭配安装选项
rpm {-U|--upgrade} [install-options] PACKAGE_FILE …
rpm {-F|--freshen} [install-options] PACKAGE_FILE ...
6.包卸载操作:
rpm {-e|--erase} [--allmatches] [--justdb] [--nodeps] [--noscripts][--notriggers] [--test] PACKAGE_NAME ...
4.通用选项:卸载、安装都适用
通用选项可以辅助安装、卸载等一级选项更详细的显示rpm的处理过程
Ø -v verbose显示详细信息,可以多个v叠加,显示不同级别的详细信息
Ø -h 显示包管理程序的处理进度
二、包管理操作
1.程序包安装
程序安装过程:
Ø 执行安装前的脚本文件,如创建用户、创建必要的目录、权限设置
Ø 解包将各种文件复制到相应的目录。
Ø rpm包安装完后会自动注册在RPM公共数据库中/var/lib/rpm,便于后期卸载、查询操作
语法:
rpm {-i|--install} [install-options] PACKAGE_FILE…
--test: 测试安装,但不真正执行安装,即dry run模式
--nodeps:忽略依赖关系,强制定包
--force 强行安装
--replacepkgs | replacefiles 替换包或文件 用于。替换原有包,覆盖安装。
--nosignature: 不检查来源合法性
--nodigest:不检查包完整性
--noscripts:不执行程序包脚本
%pre: 安装前脚本 --nopre
%post: 安装后脚本 --nopost
%preun: 卸载前脚本 --nopreun
%postun: 卸载后脚本 --nopostun
常用选项组合
Ø 覆盖原有包:修改因包文件被误删除导致重新安装失败
rpm -ivh tree-1.7.0-15.el8.x86_64.rpm --replacepkgs
Ø 覆盖原有文件:
rpm -ivh tree-1.7.0-15.el8.x86_64.rpm --replacefiles
Ø 跳过包签名校验
rpm -ivh tree-1.7.0-15.el8.x86_64.rpm --nosignature
实用小技巧
判断一个软件包是否存在,如果不存在则安装相应的软件包
rpm -q tree > /dev/null ||rpm -ivh ../../BaseOS/Packages/tree-1.7.0-15.el8.x86_64.rpm
2.程序包卸载
语法:
rpm {-e|--erase} [--allmatches] [--justdb] [--nodeps] [--noscripts]
Ø --allmatches 卸载所有匹配的包,匹配指定的关键字的包全部卸载
Ø --justdb
Ø --nodeps 忽略依赖关系强行卸载
Ø --noscripts 执行卸载过程中不执行卸载脚本
小提示: 当包卸载时,对应的配置文件不会删除, 以FILENAME.rpmsave形式保留 如httpd的http.conf文件
3.程序包升级
语法:
upgrade:安装有旧版程序包,则“升级” 如果不存在旧版程序包,则“安装”
rpm {-U|--upgrade} [install-options] PACKAGE_FILE...
freshen:安装有旧版程序包,则“升级”如果不存在旧版程序包,则不执行升级操作
rpm {-F|--freshen} [install-options] PACKAGE_FILE...
4.程序包降级
降级一般用于业务测试软件版本切换,或业务节点升级出现异常执行版本回退操作,实际上就是正常安装旧版程序包,但需要同时--oldpackage --force两个微调选项,升级后两个版包并存。可以根据实际需要决定是否卸载新版包。
rpm -ivh tree-1.6.0-10.el7.x86_64.rpm --oldpackage --force
小提示: 如果只有--oldpackage会提示文件冲突,需同时指定 --replacefiles进行文件替换。此时通过rpm -q查询会发现两个版本软件并存的现象,但后安装的应用程序文件覆盖了原有应用程序文件,软件确实降级成功。原因:-i是安装操作会将新的软件版本信息注册到rpm数据库。因此卸载、查询操作需要加版本号才可以。
5.程序包查询
坦白说个人认为rpm包管理工具查询功能在工作中用得最多了,当然yum也有查询功能,但是不大众的包还得yum与rpm配合来用
语法:rpm {-q|--query} [select-options] [query-options]
常用选项组合
Ø rpm -q httpd 查询是系统是否已经安装某包,多版并存时需指定版本号
Ø rpm -q httpd-tools --scripts查看安装包内的脚本文件
Ø rpm -qa|grep httpd all查看所有已安装的包并过滤
Ø rpm -qc httpd cofig查看某应用的配置文件位置
Ø rpm -qd httpd doc查看某应用的帮助文档位置
Ø rpm-qf /etc/passwd 查看某文件来自于哪个rpm包
Ø rpm -qi setup-2.8.71-10.el7.noarch查看已安装包的详细信息
Ø rpm -ql httpd-tools 查看已安装包产生的文件列表
Ø rpm -qpl yum-4.0.9.2-5.el8.noarch.rpm 查看未安装的rpm包内文件列表
Ø rpm -qpi yum-4.0.9.2-5.el8.noarch.rpm 查看未安装的rpm包的元数据信息
功能查询
这个能力查询功能是最长用的了
Ø --whatprovides CAPABILITY:查询指定的CAPABILITY由哪个包所提供
Ø --whatrequires CAPABILITY:查询指定的CAPABILITY被哪个包所依赖
Ø -R:查询指定的程序包所依赖的CAPABILITY
技巧举例:
Ø rpm -q httpd --provides:列出httpd提供的CAPABILITY(能力)
Ø rpm -q httpd --whatprovides:查询httpd这个能力是来自于哪个程序包
Ø rpm -q bash --whatprovides 查询bash命令是由哪个包提供的
Ø rpm -q bash --whatrequires 查询都有哪些包依赖bash这个能力
6.程序包校验
这个包校验虽然简单,但在实际应用过程中可要注意涉及安全问题,尤其是现在网络ISP为了提供用户体验可能出现域名劫持,或HTTPS阶段代理,如果我们使用yum下载安装rpm包的过程中出现了域名劫持,然后yum客户端仓库配置文件配置了gpgcheck=1,gpgkey也配置了网上获取,则如果yum源服务器域名被劫持了,那么在第一次使有yum的时候非法的key会被自动导入到系统,即使开了gpgcheck功能也是枉然。这里有个知识点要先确实一下:数据签名技术,rpm包验证也就是基本数据签名技术。与微软的数字签名技术是一样的
大概原理是将官方对rmp包做hash得到散列值A,然后用私钥加密散列值得到的密文信息做为额外属性携带在rpm包中,客户端拿到rpm包后将rpm包的数据部分做hash计算得到散列值B,然后再用官方发而的公钥解密rpm包中携带的密文得到原始散列A,如果A=B则认为该rpm包从官方发布至今未做任何修改,并且一定是官方发布的,否则公钥无法解密密文得到A。
官方发布的安装包采用的是非对称密钥加密技术,私钥加密码公钥解密-数字签名的技术,公钥导入系统后再安装官方的rpm就不会出现签名告警了,如果系统中没有导入官方公钥或已经导入公钥但每次安装rpm包都提供签名告警那就真要注意了
公钥管理:
官方的公钥随光盘携带,需要导入系统:
Ø 安装公钥:rpm --import RPM-GPG-KEY-CentOS-7
Ø 搜索公钥:rpm -qa "*pubkey*"
Ø 查看公钥:rpm -qi gpg-pubkey-f4a80eb5-53a7ff4b
Ø CentOS中存储位置为:/etc/pki/rpm-gpg
Ø 卸载公钥:rpm -e gpg-pubkey
校验程序文件属性变化:从系统安装到查询时间点开始计算发生变化就会显示打上下标记
S file Size differs
M Mode differs (includes permissions and file type)
5 digest (formerly MD5 sum) differs
D Device major/minor number mismatch
L readLink(2) path mismatch
U User ownership differs
G Group ownership differs
T mTime differs
P capabilities differ
rpm -V tree
rpm解包操作
rpm2cpio /misc/cd/BaseOS/Packages/tree-1.7.0-15.el8.x86_64.rpm |cpio -idv ./
三、rpm微调选项
select-options 作为包查询、校验操作的微调选项
[PACKAGE_NAME]
[-a,--all [SELECTOR]] [-f,--file FILE]
[-g,--group GROUP] {-p,--package PACKAGE_FILE]
[--hdrid SHA1]
[--pkgid MD5]
[--tid TID]
[--querybynumber HDRNUM]
[--triggeredby PACKAGE_NAME]
[--whatprovides CAPABILITY] 查询指定的CAPABILITY由哪个程序包提供
[--whatrequires CAPABILITY] 查询哪些程序包依赖此CAPABILITY
[--whatrecommends CAPABILITY]
[--whatsuggests CAPABILITY]
[--whatsupplements CAPABILITY]
[--whatenhances CAPABILITY]
[--whatobsoletes CAPABILITY]
[--whatconflicts CAPABILITY]
query-options 作为包查询操作的微调选项
General: 常规查询选项
[--changelog] 显示包的更改记录
[--changes]
[--dupes]
[-i,--info]显示包信息
[--last]
[--qf,--queryformat QUERYFMT]
[--xml]
Dependencies:
[--conflicts]
[--enhances]
[--obsoletes]
[--provides]
[--recommends]
[-R,--requires]
[--suggests]
[--supplements]
Files:文件类查询
[-c,--configfiles] 查询已安装包的配置文件存储位置 例:rpm -qc bash
[-d,--docfiles] 查询已安装包的帮助文档存储位置 例:rpm -qd bash
[--dump]
[--fileclass] 查询已安装包相关文件的类型 rpm -q bash --fileclass
[--filecolor]
[--fileprovide]
[--filerequire] rpm -q yum --filerequire
[--filecaps]
[--filesbypkg]
[-l,--list] 查看包相关的文件列表
[-s,--state] 查看包相关的文件状态
[--noartifact]
[--noghost]
[--noconfig
Scripts and triggers:
[--filetriggers]
[--scripts] 查看包安装前后执行的脚本 rpm -q bash --scripts
[--triggers,--triggerscripts]
verify-options
[--nodeps] 不校验软件的依赖关系
[--nofiles]
[--noscripts]
[--nodigest]
[--nosignature] 不校验数字签名
[--nolinkto]
[--nofiledigest]
[--nosize]
[--nouser]
[--nogroup]
[--nomtime]
[--nomode]
[--nordev]
[--nocaps]
install-options
[--allfiles] 所有包
[--badreloc]
[--excludepath OLDPATH]
[--excludedocs]
[--force] 强制安装,--replacefiles和—replacepkgs的综合体
[-h,--hash]
[--ignoresize]
[--ignorearch] 忽略硬件架构平台
[--ignoreos] 忽略操作系统
[--includedocs]
[--justdb] 更新数据库,当不变动任何文件
[--nodeps] 忽略依赖关系直接安装
[--nodigest] 不校验包完整性
[--noplugins]
[--nocaps]
[--noorder]
[--noverify]
[--nosignature] 忽略验数字签名,用于安装第三方包
[--noscripts] 不执行软件包内的安装前后脚本
[--notriggers]
[--oldpackage] 用于软件包降级
[--percent] 安装时显示完成度百分比
[--prefix NEWPATH] 指定安装目录,把文件放到指定的目录下
[--relocate OLDPATH=NEWPATH] 把本来会放到原目录下的文件改放到新目录。
[--replacefiles] 替换文件
[--replacepkgs] 替换包
[--test] 测试安装,但不真正执行安装,即dry run模式