在linux中,編譯安裝是指將原始碼根據機器本身的硬體、核心、環境變成機器可執行的程式碼文件,然後將執行檔安裝到作業系統裡。因為是針對本機軟硬體環境編譯的,產生的二進位程式運行起來理論上效能更好,更節省資源。
本教學操作環境:linux7.3系統、Dell G3電腦。
編譯:將原始程式碼變成機器可執行的程式碼檔案。
安裝:將執行檔安裝到作業系統裡,才可以使用。
編譯安裝,就是使用原始碼,根據機器本身的硬體、核心、環境進行編譯,產生二進位文件,優點是不管什麼機器,只要有完整的編譯環境(基本上所有的Linux發行版都有自己的一套完整的編譯環境),就可以產生適合自己機器的二進位包,同時因為是針對本機軟硬體環境編譯的,生成的二進位程式運行起來理論上性能更好,更節省資源。
缺點:是對新手來說編譯過程比較麻煩(當然如果你熟悉了其實也很快),同時升級比較麻煩(當然有些程式編譯安裝過程會自己解決升級衝突和覆蓋)
說明:
源碼要運行,必須先轉成二進位的機器碼。這是編譯器的任務。
例如,下面這段原始碼(假定檔案稱為做test.c)。
#include <stdio.h> int main(void) { fputs("Hello, world!\n", stdout); return 0; }</stdio.h>
要先用編譯器處理一下,才能運作。
$ gcc test.c $ ./a.out Hello, world!
對於複雜的項目,編譯過程也必須分成三個步驟。
$ ./configure $ make $ make install
編譯流程圖:
編譯器在開始工作之前,需要知道目前的系統環境,例如標準函式庫在哪裡、軟體的安裝位置在哪裡、需要安裝哪些元件等等。這是因為不同電腦的系統環境不一樣,透過指定編譯參數,編譯器就可以靈活適應環境,編譯出各種環境都能運作的機器碼。這個決定編譯參數的步驟,就叫做"配置"(configure)。
這些設定檔保存在一個設定檔之中,約定俗成是一個叫做configure的腳本檔。通常它是由autoconf工具產生的。編譯器透過執行這個腳本,獲知編譯參數。
configure腳本已經盡量考慮到不同系統的差異,並且對各種編譯參數給出了預設值。如果使用者的系統環境比較特別,或有一些特定的需求,就需要手動向configure腳本提供編譯參數。
$ ./configure --prefix=/www --with-mysql
上面程式碼是php原始碼的一種編譯配置,使用者指定安裝後的檔案保存在www目錄,並且編譯時加入mysql模組的支援。
原始碼肯定會用到標準函式庫函數(standard library)和頭檔(頭檔)。它們可以存放在系統的任意目錄中,編譯器實際上沒辦法自動檢測它們的位置,只有透過設定檔才能知道。
編譯的第二步,就是從設定檔知道標準函式庫和頭檔的位置。一般來說,設定檔會給予一個清單,列出幾個特定的目錄。等到編譯時,編譯器就依序到這幾個目錄中,尋找目標。
對於大型專案來說,原始碼檔案之間往往存在依賴關係,編譯器需要確定編譯的先後順序。假定A文件依賴B文件,編譯器應該確保做到下面兩點。
(1)只有在B文件编译完成后,才开始编译A文件。 (2)当B文件发生变化时,A文件会被重新编译。
編譯順序保存在一個叫做makefile的檔案中,裡面列出哪個檔案先編譯,哪個檔案後編譯。而makefile檔案則是由configure腳本執行生成,這就是為什麼編譯時configure必須先執行的原因。
在決定依賴關係的同時,編譯器也確定了,編譯時會用到哪些頭檔。
#不同的原始碼文件,可能引用同一個頭檔(例如stdio.h)。編譯的時候,頭檔也必須一起編譯。為了節省時間,編譯器會在編譯原始碼之前,先編譯頭檔。這保證了頭檔只需編譯一次,不必每次用到的時候,都重新編譯了。
不過,並不是頭檔的所有內容,都會被預先編譯。用來宣告巨集的#define指令,就不會被預先編譯。
预编译完成后,编译器就开始替换掉源码中bash的头文件和宏。以本文开头的那段源码为例,它包含头文件stdio.h,替换后的样子如下。
extern int fputs(const char *, FILE *); extern FILE *stdout; int main(void) { fputs("Hello, world!\n", stdout); return 0; }
为了便于阅读,上面代码只截取了头文件中与源码相关的那部分,即fputs和FILE的声明,省略了stdio.h的其他部分(因为它们非常长)。另外,上面代码的头文件没有经过预编译,而实际上,插入源码的是预编译后的结果。编译器在这一步还会移除注释。
这一步称为"预处理"(Preprocessing),因为完成之后,就要开始真正的处理了。
预处理之后,编译器就开始生成机器码。对于某些编译器来说,还存在一个中间步骤,会先把源码转为汇编码(assembly),然后再把汇编码转为机器码。
下面是本文开头的那段源码转成的汇编码。
.file "test.c" .section .rodata .LC0: .string "Hello, world!\n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq stdout(%rip), %rax movq %rax, %rcx movl $14, %edx movl $1, %esi movl $.LC0, %edi call fwrite movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Debian 4.9.1-19) 4.9.1" .section .note.GNU-stack,"",@progbits
这种转码后的文件称为对象文件(object file)。
注:make (gcc), 其调用 gcc 执行编译的过程依赖于配置文件makefile
对象文件还不能运行,必须进一步转成执行文件。如果你仔细看上一步的转码结果,会发现其中引用了stdout函数和fwrite函数。也就是说,程序要正常运行,除了上面的代码以外,还必须有stdout和fwrite这两个函数的代码,它们是由C语言的标准库提供的。
编译器的下一步工作,就是把外部函数的代码(通常是后缀名为.lib和.a的文件),添加到可执行文件中。这就叫做连接(linking)。这种通过拷贝,将外部函数库添加到可执行文件的方式,叫做静态连接(static linking),后文会提到还有动态连接(dynamic linking)。
make命令的作用,就是从第四步头文件预编译开始,一直到做完这一步。
上一步的连接是在内存中进行的,即编译器在内存中生成了可执行文件。下一步,必须将可执行文件保存到用户事先指定的安装目录。
表面上,这一步很简单,就是将可执行文件(连带相关的数据文件)拷贝过去就行了。但是实际上,这一步还必须完成创建目录、保存文件、设置权限等步骤。这整个的保存过程就称为"安装"(Installation)。
可执行文件安装后,必须以某种方式通知操作系统,让其知道可以使用这个程序了。比如,我们安装了一个文本阅读程序,往往希望双击txt文件,该程序就会自动运行。
这就要求在操作系统中,登记这个程序的元数据:文件名、文件描述、关联后缀名等等。Linux系统中,这些信息通常保存在/usr/share/applications目录下的.desktop文件中。另外,在Windows操作系统中,还需要在Start启动菜单中,建立一个快捷方式。
这些事情就叫做"操作系统连接"。make install命令,就用来完成"安装"和"操作系统连接"这两步。
写到这里,源码编译的整个过程就基本完成了。但是只有很少一部分用户,愿意耐着性子,从头到尾做一遍这个过程。事实上,如果你只有源码可以交给用户,他们会认定你是一个不友好的家伙。大部分用户要的是一个二进制的可执行程序,立刻就能运行。这就要求开发者,将上一步生成的可执行文件,做成可以分发的安装包。
所以,编译器还必须有生成安装包的功能。通常是将可执行文件(连带相关的数据文件),以某种目录结构,保存成压缩文件包,交给用户。
正常情况下,到这一步,程序已经可以运行了。至于运行期间(runtime)发生的事情,与编译器一概无关。但是,开发者可以在编译阶段选择可执行文件连接外部函数库的方式,到底是静态连接(编译时连接),还是动态连接(运行时连接)。所以,最后还要提一下,什么叫做动态连接。
前面已经说过,静态连接就是把外部函数库,拷贝到可执行文件中。这样做的好处是,适用范围比较广,不用担心用户机器缺少某个库文件;缺点是安装包会比较大,而且多个应用程序之间,无法共享库文件。动态连接的做法正好相反,外部函数库不进入安装包,只在运行时动态引用。好处是安装包会比较小,多个应用程序可以共享库文件;缺点是用户必须事先安装好库文件,而且版本和安装位置都必须符合要求,否则就不能正常运行。
现实中,大部分软件采用动态连接,共享库文件。这种动态共享的库文件,Linux平台是后缀名为.so的文件,Windows平台是.dll文件,Mac平台是.dylib文件。
1.编译安装源程序的前提:
1).提供开发环境:开发工具和开发库
2).编译安装需要的包组:
Development Tools、Server Platform Development、Desktop Platform Development、Debug Tools
2.configure脚本常用的选项:
--help获取./configure脚本帮助 --prefix=: 指定安装路径;多数程序都有默认安装路径; --sysconfidr=: 指定配置文件安装路径; --with-PACKAGE[=ARG]:在自由软件社区里,有使用已有软件包和库的优秀传统.当用'configure'来配置一个源码树时, 可以提供其他已经安装的软件包的信息 --without-PACKAGE:有时候你可能不想让你的软件包与系统已有的软件包交互。例如,你可能不想让你的新编译器使用 GNU ld --enable-FEATURE:一些软件包可能提供了一些默认被禁止的特性,可以使用'--enable-FEATURE'来起用它 --disable-EEATURE:关闭指定的默认特性
3.编译安装源程序方法:
1)、展开源代码,找INSTALL、README;不存在此类文件时,找项目官方文档;
2)、根据安装说明执行安装操作;
4.程序安装于专用目录时,安装后的配置:
1)、导出二进制程序所在路径至PATH环境中
# export PATH=/usr/local/nginx/sbin:$PATH 实现永久有效的办法: /etc/profile.d/*.sh
2)、导出库文件给OS
OS查找库文件方法:根据/etc/ld.so.conf配置文件指定的路径搜索,或搜索/lib, /lib64, /usr/lib, /usr/lib64,把查找到的所有的库文件路径和其名称映射关系保存为一个缓存文件/etc/ld.so.cache;
/etc/ld.so.conf配置文件有其它组成部分:/etc/ld.so.conf.d/*.conf
假设nginx安装于/usr/local/nginx,此目录中有其库文件子目录lib,导出此目录中库文件:
(1)新建文件/etc/ld.so.conf.d/nginx.conf,在文件添加如下行:
/usr/local/nginx/lib
(2)运行命令:ldconfig
ldconfig的主要用途:
默认搜寻/lilb和/usr/lib,以及配置文件/etc/ld.so.conf内所列的目录下的库文件。
搜索出可共享的动态链接库,库文件的格式为:lib***.so.**,进而创建出动态装入程序(ld.so)所需的连接和缓存文件。
缓存文件默认为/etc/ld.so.cache,该文件保存已排好序的动态链接库名字列表。
ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。
常用选项:
-v: 用此选项时,ldconfig将显示正在扫描的目录及搜索到的动态链接库,还有它所创建的连接的名字. -p: 显示当前OS已经加载到的所有库文件名称及其文件所在路径的映射关系;
ldconfig需要注意的地方:
(a)、往/lib和/usr/lib里面加东西,是不用修改/etc/ld.so.conf文件的,但是添加完后需要调用下ldconfig,不然添加的library会找不到。
(b)、如果添加的library不在/lib和/usr/lib里面的话,就一定要修改/etc/ld.so.conf文件,往该文件追加library所在的路径,然后也需要重新调用下ldconfig命令。比如在安装mysql的时候,其库文件/usr/local/mysql/lib,就需要追加到/etc/ld.so.conf文件中。命令如下:
# echo "/usr/local/mysql/lib" >> /etc/ld.so.conf # ldconfig -v | grep mysql
(c)、如果添加的library不在/lib或/usr/lib下,但是却没有权限操作写/etc/ld.so.conf文件的话,这时就需要往export里写一个全局变量LD_LIBRARY_PATH,就可以了。
(3)、帮助文件导出
man命令搜索特定路径查找手册页文件,这些路径是定义在/etc/man.config中的MANPATH参数所指定的路径下的;
新增办法:编辑/etc/man.config文件,新增一个MANPATH参数,其值为新安装程序的man手册所在的目录;
/usr/local/nginx/share/man/{man1,man8} man -M /path/to/man KEYWORD
(4)、头文件导出
有些程序安装后会生成对自己拥有库文件调用接口相关头文件系统查找头文件的路径为/usr/include
导出独立安装应用程序的头文件方法:创建链接至/usr/include下即可;
例如:
/usr/local/nginx/include # ln -sv /usr/local/nginx/include/* /usr/include/ # ln -sv /usr/local/nginx/include /usr/include/nginx
perl源程序的编译安装方法:
(1) perl Makefile.in (2) make (3) make install
相关推荐:《Linux视频教程》
以上是linux什麼是編譯安裝的詳細內容。更多資訊請關注PHP中文網其他相關文章!