日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

Linux下被我誤解的gcc,軟件可執行文件的跨系統版本兼容性沒有那么差,如果你也是這樣處理

來源: 責編: 時間:2023-12-22 17:13:49 304觀看
導讀[開發]為什么不建議交付靜態鏈接的可執行文件給用戶?為什么Golang開發的軟件單文件直接丟到各種Linux系統就能運行?[開發+運維] Linux從外到內剝開動態庫,1個簡單例子看懂Linux下的動態庫開發原理以上是近期發布的這3
  • [開發]為什么不建議交付靜態鏈接的可執行文件給用戶?
  • 為什么Golang開發的軟件單文件直接丟到各種Linux系統就能運行?
  • [開發+運維] Linux從外到內剝開動態庫,1個簡單例子看懂Linux下的動態庫開發原理

以上是近期發布的這3篇文章,圍繞Linux下的可執行文件究竟是采用靜態鏈接還是動態鏈接方式發布,從不同角度做了分析。看起來第1、2篇的觀點是相反的。現在對這兩個看似矛盾的說法加以解釋,以期讀者能對Linux的動態庫、靜態編譯可執行文件的理解,和我一樣在學習進步,形成總體符合實際的認知。H1D28資訊網——每日最新資訊28at.com

也得到了讀者的很多反饋,現舉例其一:H1D28資訊網——每日最新資訊28at.com

H1D28資訊網——每日最新資訊28at.com

為什么要盯住gcc呢?因為Linux的軟件生態中,C/C++開發的軟件,特別是基礎型軟件、需要高性能的軟件,往往歷史原因都以這兩種語言為主,在此不贅述。H1D28資訊網——每日最新資訊28at.com

總之,因為C/C++開發的軟件占比很大,即使開發業務中不直接使用,也會在運行中依賴到C/C++的庫或可執行文件的功能。搞懂動態庫的特性,對于解決一些軟件依賴問題、開發編譯鏈接失敗問題,都有幫助。所以開發者和運維仍然有必要增進這方面的理解。H1D28資訊網——每日最新資訊28at.com

1.動態鏈接適合插件化開發、插件化升級、希望打包發布的可執行文件盡量小的場景;而靜態鏈接方式適合易部署、不想處理第三方動態庫依賴問題的場景。H1D28資訊網——每日最新資訊28at.com

2.gcc/g++ 作為Linux下主要的編譯器,支持動態鏈接、靜態鏈接方式。如最基本的main.c 代碼可通過gcc -o main_dynamic_link main.c 和 gcc -static -o main_static main.c 分別得到兩類可執行文件。這是大學生在學校初學Linux下的gcc C/C++編程的時候就了解的。H1D28資訊網——每日最新資訊28at.com

3.gcc 動態鏈接生成的可執行文件,因為代碼必然使用到c/c++的標準庫提供的函數,那么可執行文件必然要與libc.so庫動態庫鏈接(如下圖)。H1D28資訊網——每日最新資訊28at.com

H1D28資訊網——每日最新資訊28at.com

4.gcc編譯得到的可執行文件,運行時會以進程方式在用戶態、內核態的內存中布局。如果可執行文件是動態鏈接方式的,則運行時由 Linux內核負責載入ELF格式的可執行文件后,內核通過 ld-linux.so (64位系統下則為 ld-linux-x86-64.so ) 分析可執行文件依賴的其他動態庫信息,由ld-linux.so 負責逐個載入其他動態庫到該進程的虛擬內存的代碼段位置中。這里就發揮了動態庫和虛擬內存的優勢:熱門的動態庫被很多其他進程依賴,那么這種可執行文件實際只占用物理內存的一塊空間,無論被多少個進程依賴。H1D28資訊網——每日最新資訊28at.com

所以達到了提高內存利用率的效果,這對于需要運行大量軟件的場景(如Linux桌面),收益還是可觀的。可執行文件從被調起到執行完畢,我們可以用 strace 命令看到全過程,包括需要讀取的其他庫文件的過程。比如下面的可執行文件可以用strace 看到執行全流程(功能只調用printf函數打印字符串)。H1D28資訊網——每日最新資訊28at.com

root@localhost:~# file  ./main./main: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=82e7afc31da1cdbdd374658c2724dce983ccedab, for GNU/Linux 3.2.0, not strippedroot@localhost:~# ldd  ./main        not a dynamic executable   #說明當前是靜態鏈接的                root@localhost:~# strace ./mainexecve("./main", ["./main"], 0x7ffe6fd2a090 /* 25 vars */) = 0arch_prctl(0x3001 /* ARCH_??? */, 0x7ffe7ada0370) = -1 EINVAL (Invalid argument)brk(NULL)                               = 0x1a12000brk(0x1a12dc0)                          = 0x1a12dc0arch_prctl(ARCH_SET_FS, 0x1a123c0)      = 0set_tid_address(0x1a12690)              = 27080set_robust_list(0x1a126a0, 24)          = 0rseq(0x1a12d60, 0x20, 0, 0x53053053)    = 0uname({sysname="Linux", nodename="localhost", ...}) = 0prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0readlink("/proc/self/exe", "/root/main", 4096) = 10getrandom("/xa1/xe6/x48/xa4/x1d/x32/xef/x0e", 8, GRND_NONBLOCK) = 8brk(0x1a33dc0)                          = 0x1a33dc0brk(0x1a34000)                          = 0x1a34000mprotect(0x4c1000, 16384, PROT_READ)    = 0newfstatat(1, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0), ...}, AT_EMPTY_PATH) = 0write(1, "111", 3111)                      = 3exit_group(3)                           = ?+++ exited with 3 +++## 下面按動態鏈接生成可執行文件root@localhost:~# gcc  -o main main.c## strace 顯示可執行文件 執行時需要加載 `/lib/x86_64-linux-gnu/libc.so.6` 文件。root@localhost:~# strace ./mainexecve("./main", ["./main"], 0x7ffec2fb69c0 /* 25 vars */) = 0brk(NULL)                               = 0x55fd3f1bc000root@localhost:~#mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa3491dd000access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=87359, ...}, AT_EMPTY_PATH) = 0mmap(NULL, 87359, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa3491c7000close(3)                                = 0openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3read(3, "/177ELF/2/1/1/3/0/0/0/0/0/0/0/0/3/0>/0/1/0/0/0P/237/2/0/0/0/0/0"..., 832) = 832pread64(3, "/6/0/0/0/4/0/0/0@/0/0/0/0/0/0/0@/0/0/0/0/0/0/0@/0/0/0/0/0/0/0"..., 784, 64) = 784pread64(3, "/4/0/0/0 /0/0/0/5/0/0/0GNU/0/2/0/0/300/4/0/0/0/3/0/0/0/0/0/0/0"..., 48, 848) = 48pread64(3, "/4/0/0/0/24/0/0/0/3/0/0/0GNU/0 =/340/2563/265?/356/25x/261/27/313A#/350"..., 68, 896) = 68newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2216304, ...}, AT_EMPTY_PATH) = 0pread64(3, "/6/0/0/0/4/0/0/0@/0/0/0/0/0/0/0@/0/0/0/0/0/0/0@/0/0/0/0/0/0/0"..., 784, 64) = 784mmap(NULL, 2260560, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa348f9f000mmap(0x7fa348fc7000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7fa348fc7000mmap(0x7fa34915c000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7fa34915c000mmap(0x7fa3491b4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x214000) = 0x7fa3491b4000mmap(0x7fa3491ba000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa3491ba000close(3)                                = 0mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa348f9c000arch_prctl(ARCH_SET_FS, 0x7fa348f9c740) = 0set_tid_address(0x7fa348f9ca10)         = 27089set_robust_list(0x7fa348f9ca20, 24)     = 0rseq(0x7fa348f9d0e0, 0x20, 0, 0x53053053) = 0mprotect(0x7fa3491b4000, 16384, PROT_READ) = 0mprotect(0x55fd3e9e1000, 4096, PROT_READ) = 0mprotect(0x7fa34921d000, 8192, PROT_READ) = 0prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0munmap(0x7fa3491c7000, 87359)           = 0newfstatat(1, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0), ...}, AT_EMPTY_PATH) = 0getrandom("/x84/x8c/x06/x16/x25/xe5/x97/x97", 8, GRND_NONBLOCK) = 8brk(NULL)                               = 0x55fd3f1bc000brk(0x55fd3f1dd000)                     = 0x55fd3f1dd000write(1, "111", 3111)                      = 3exit_group(3)                           = ?+++ exited with 3 +++root@localhost:~#

可以看到 動態鏈接的C可執行文件運行時確實需要加載 /lib/x86_64-linux-gnu/libc.so.6 文件。而靜態鏈接的可執行文件沒有加載任何.so文件,包括libc.so也不需要。因為靜態文件的代碼指令已完整在文件中。H1D28資訊網——每日最新資訊28at.com

root@localhost:~# gcc --versiongcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0Copyright (C) 2021 Free Software Foundation, Inc.This is free software; see the source for copying conditions.  There is NOwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.root@localhost:~# gcc -static -o main_static main.c  # 靜態鏈接root@localhost:~# gcc  -o main_shared_link main.c    # 動態鏈接root@localhost:~# ls -lhttotal 1.8M-rwxr-xr-x 1 root root  16K Dec 17 23:58 main_shared_link-rwxr-xr-x 1 root root 880K Dec 17 23:58 main_static-rw-r--r-- 1 root root   54 Dec 17 22:30 main.croot@localhost:~# file main_static   # 查看靜態鏈接文件的屬性main_static:      ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=82e7afc31da1cdbdd374658c2724dce983ccedab, for GNU/Linux 3.2.0, not strippedroot@localhost:~# main_shared_link  # 查看動態鏈接文件的屬性main_shared_link: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f14401e673b52624535874f2e1a8488a0edbc891, for GNU/Linux 3.2.0, not strippedroot@localhost:~# ls -lht /lib/x86_64-linux-gnu/libc.so.6-rwxr-xr-x 1 root root 2.2M Nov 22 21:18 /lib/x86_64-linux-gnu/libc.so.6

這里看到:880K Dec 17 23:58 main_static靜態文件大小880K,而libc.so大小2.2M 。靜態文件大小比C標準庫動態文件的大小還小很多。H1D28資訊網——每日最新資訊28at.com

說明gcc并不只是把libc.so的所有函數完全包含進靜態可執行文件,而這是我之前對gcc的誤解。H1D28資訊網——每日最新資訊28at.com

實際gcc對靜態的鏈接是做了精簡優化的,只保留了有被調用的函數代碼到最終可執行文件內。H1D28資訊網——每日最新資訊28at.com

5.所以本文最初提到的《為什么不建議交付靜態鏈接的可執行文件給用戶?》 中的不建議,是只從靜態文件的分發方式降低了系統基礎庫的重用率的角度出發的。當然隨著技術的進步,也許以后內核的可執行文件加載器能做到識別出靜態文件中的某些可替代的公共部分就自動剔除對物理內存的占用,而借助系統已有的已載入內存的公共代碼實現替代,而不影響軟件功能,就結合了動態和靜態的兩種優勢。這只是一個猜想,并非合理完美的方案。H1D28資訊網——每日最新資訊28at.com

6.而《為什么Golang開發的軟件單文件直接丟到各種Linux系統就能運行?》中golang能做到的,gcc也能做到,所以C/C++靜態鏈接的可執行文件的跨系統版本兼容性沒有那么差,跟Golang生成的靜態可執行文件是一樣能丟到各種Linux發行版的運行的。H1D28資訊網——每日最新資訊28at.com

7.但跨系統兼容性的前提是32位、64位系統和軟件的數位,要匹配。除非32位的系統內核支持PAE特性或Linux系統額外安裝了 multilib 庫,以實現32位系統下運行64位的軟件,或64位系統下運行32位的軟件。H1D28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-52183-0.htmlLinux下被我誤解的gcc,軟件可執行文件的跨系統版本兼容性沒有那么差,如果你也是這樣處理

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: JS問題:如何實現文本一鍵復制和長按復制功能?

下一篇: 在三分鐘內學習二分查找

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 阿瓦提县| 远安县| 平罗县| 报价| 张北县| 洛宁县| 阳新县| 沁水县| 平江县| 湘潭市| 滦南县| 徐水县| 民勤县| 黔西县| 双鸭山市| 景洪市| 澜沧| 密云县| 莲花县| 通河县| 龙里县| 涟源市| 留坝县| 东光县| 宜君县| 宣汉县| 舞钢市| 元氏县| 富民县| 吉隆县| 望谟县| 鸡东县| 登封市| 民丰县| 万盛区| 克东县| 马尔康县| 宝清县| 河南省| 确山县| 织金县|