Linux->動靜態庫

目錄

引入:

一:動靜態庫的介紹

1:庫的本質

2:庫的類別及優缺點

3:動態鏈接

4:靜態鏈接

二:頭文件和庫的查找

三:靜態庫的制作和使用

1:制作

2:指令打包

3:makefile打包

4:使用

①:直接gcc

②:僅-I

③:僅-I -L

④:三個選項齊全

四:動態庫的制作和使用

1:制作

2:指令打包

3:makefile打包

4:使用

五:系統查找動態庫

1:拷貝到默認搜索路徑下

2:修改環境變量修改

3:軟鏈接

4:動態庫路徑配置文件

六:動靜態庫的優先級規則

七:動態庫的加載

1:共享區的作用

2:編址

3:動態庫的加載過程


引入:

本文雖會詳細介紹動靜態庫的相關知識,但是gcc的相關語法以及編譯的細分4小步,和此篇博客的一大點的內容,也是在下面博客中說過了:Linux環境基礎開發工具->gcc/g++-CSDN博客

所以一定要看,不然肯定看不懂此篇博客

一:動靜態庫的介紹

1:庫的本質

函數所處的.c文件編譯后會形成.o文件,當多個.c形成多個.o后,這多個.o的集合就叫做庫!!

多個.o并不是放在那里就是一個庫,要分別用不同的方法才能讓這多個.o去形成動態庫或靜態庫!(在后面會講解)

2:庫的類別及優缺點


庫一般分為靜態庫和動態庫兩種:

①:靜態庫是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件當中,因此生成的文件比較大,但在運行時也就不再需要庫文件了,靜態庫一般以.a為后綴。


②:動態庫與之相反,在編譯鏈接時并沒有把庫文件的代碼加入到可執行文件當中,而是在程序運行時由鏈接文件加載庫,這樣可以節省系統的開銷,動態庫一般以.so為后綴。

二者的優缺點:

動態鏈接:
?優點:省空間(磁盤的空間,內存的空間),且體積小,加載速度快。
?缺點:依賴動態庫,程序可移植性較差。

靜態鏈接
?優點:不依賴第三方庫,程序的可移植性較高。
?缺點:浪費空間。

3:動態鏈接


Q:那我們寫的.c 和 .cpp文件在gcc進行鏈接操作的時候,是鏈接的哪一種庫?

A:file指令就能知道

解釋:dynamically linked 意味著動態鏈接,所以鏈接的是動態庫!

4:靜態鏈接


我們還可以強行對code.o文件進行靜態鏈接到庫,指令:gcc -static

注:當然也可以直接對code.c進行強行的靜態鏈接,自由選擇即可

gcc code.o -o code.out_static -static//選項-static

我們靜態鏈接生成的可執行文件取名為?code.out_static,方便區別于code.out

此時我們就有兩個可執行文件了:

解釋:由靜態鏈接的缺點可知,靜態鏈接是把庫文件的代碼全部加入到可執行文件當中,所以這就是為什么我們的code.out_static的大小是861288,遠遠大于code.out的原因!

我們可以通過ldd來查看code.out_static是否真的被靜態鏈接了:

解釋:?信息告訴我們:這個程序是靜態鏈接的,沒有依賴任何動態庫(.so?文件),因此無法列出動態庫依賴關系;所以這正好說明了的確進行了靜態鏈接

再通過file來看一下code.out_static:

解釋:statically linked 意味著靜態鏈接

總結:Linux中的編譯器gcc和g++默認都是動態鏈接的!需要靜態鏈接需要加 -static選項

二:頭文件和庫的查找

我們在使用庫中方法的時候,第一件事情,就是要找到方法所處的庫和聲明方法的頭文件,這一點,是毋庸置疑的!不然你的程序怎么跑?你用的方法,找不到聲明和實現,肯定出錯,而且是鏈接式報錯!

Q1:為什么是鏈接式報錯

A1:因為,鏈接的時候,就是找庫的時候,所以找不到庫,會報鏈接錯誤!

而當我們進行gcc的時候,直接生成了可執行程序,仿佛我們不用找庫,不用找頭文件?
其實不然,gcc只是幫我們做了這些事情,所以下面我們要了解gcc是怎么去查找庫和頭文件的!!!!

查找頭文件:

gcc會去兩個地方查找,一是默認搜索路徑,二是main函數所處的.c文件的當前目錄

查找庫:

gcc只會默認搜索路徑下查找

Q2:默認搜索路徑是什么?

A2:頭文件的默認搜索路徑就是Linux默認存儲頭文件的目錄的絕對地址;同理,庫的默認搜索路徑就是Linuxx默認存儲庫的目錄的絕對地址!

這就是為什么我們在Linux下的代碼使用了C方法或者C++方法的時候,只需gcc編譯,就能形成可執行程序,因為gcc能夠在這兩個搜索路徑下找到對應的庫和頭文件!

所以像C和C++的庫,都是已經被放在默認搜索路徑中的!

Q3:你講這么多,我理解了去默認搜索路徑下找頭文件和庫,但是你還說頭文件還能在main函數所處的.c的同級目錄下找,這是什么意思?

A3:這點在后面會驗證,但是現在也可以進行解釋,我們用一個在vs中編寫代碼的例子來解釋,

add.h

#pragma onceint add(int, int);

add.c

int add(int a, int b)
{return a + b;
}

main.c

#include<stdio.h>
#include"add.h"int main()
{int sum = add(1, 2);printf("%d", sum);return 0;
}

運行結果:

?

這個例子中,我們頭文件并不在什么默認搜索路徑下,而是就在我們的main.c的同級目錄下,如下:

但是我們的程序依舊正確的運行起來,這就證明了在編譯的時候,其還會在當前目錄下找頭文件!

而且在main.c中,我們包含add.h的格式,和包含stdio.h的格式不同:

#include<stdio.h>
#include"add.h"

這是官方對于默認搜索路徑下的頭文件,和在當前目錄下的頭文件,提供的不同的格式,這也證明了的確是有在當前目錄下找頭文件的行為

總結:

查找頭文件:

①:默認搜索路徑

②:當前目錄

查找庫:

①:默認搜索路徑下查找

而以上這些都是對標準庫和標準庫對應的頭文件的查找方法,這些是Linux已經存儲好的文件,所以按照上面的查找方法必然都能夠找到!

那請問如果是我們自己制作出的庫和頭文件呢,很顯然,其按照這些方法,是必然找不到的!因為自己做的庫和頭文件不會在默認的搜索路徑下!

在下面的第三和第四大點中,我會自己去實現一個靜態庫和動態庫,并且能夠正確的使用自己做出來的庫,所以不妨在這里,就先介紹一下,如果讓gcc找到我們自己定義的庫和頭文件!

三個選項:

  • -I指定頭文件搜索路徑 (大寫字母 i)
  • -L指定庫文件搜索路徑?(大寫字母 L)
  • -l指明在-L的路徑下的哪一個庫??-l(小寫字母 l)

當我們的gcc帶上這三個選項,并且在選項后面跟著正確的路徑或庫名字的時候,就能夠找到我們自己制作的庫和頭文件!因為我們已經清晰的告訴了gcc我們的頭文件和庫在哪!

三:靜態庫的制作和使用

1:制作

首先我們已經介紹了庫是一堆.o文件的集合,而靜態庫讓這堆.o集合形成靜態庫的方法就是打包!

指令為:

ar -rc//不存在則創建該.a靜態庫 存在則替換該.a靜態庫

現在有這么一個場景,我寫了一個加法和減法的.c和.h,如下:

add.h:

int add(int, int);

add.c:

int add(int a, int b)
{return a + b;
}

sub.h:

int sub(int, int);

sub.c:

int sub(int a, int b)
{return a - b;
}

現在有一個用戶,他需要我寫的加法和減法,所以我就要把我的這兩個方法打包為一個庫給他,然后再把頭文件也給他,這樣他就能直接使用我的方法了

但是任何的方法或者函數,都不是直接把.c給用戶,而是將其變成.o后打包成庫給用戶

原因主要是兩點:

①:保護我們的實現代碼(因為.o文件都是二進制文件,所以起到保護作用;)

②:讓客戶操作簡單(直接打包成庫給用戶,用戶直接使用即可,而不是還要手動的自己打包成庫)

所以現在我們先讓這兩個.c形成.o:

gcc -c add.c
gcc -c sub.c

下一步我們應該進行打包,但是我們可以先嘗試下不打包以體現對于用戶的不便性!

現在來了個用戶,其已經寫好了main.c了,就差我們把所需的東西給他了:

注:main.c在user的下級目錄

用戶的main.c如下:

//用戶的main.c#include<stdio.h>
#include"add.h"
#include"sub.h"int main()
{int sum = add(20,10);int dif = sub(20,10);printf("sum=%d  dif=%d\n",sum,dif);return 0;
}

?所以既然我們不打包,我們就直接把兩個.o和兩個.h給他:

cp add.o ./user
cp sub.o ./user
cp add.h ./user
cp sub.h ./user

?

現在用戶就要開始形成自己的可執行程序了,所以其要先把自己的main.c形成.o

gcc -c main.c //-c 代表把.c形成.o文件

然后現在.o文件都有了,下一步就是鏈接形成可執行程序了:

gcc main.o add.o sub.o  //gcc形成可執行程序

?

成功的生成了a.out這個可執行程序,運行效果如下:

解釋:達到了用戶所需的效果

但是如果某個場景中,.o文件多達數百個,用戶在網站中下載我們這一大堆零散的.o文件,出現遺漏,那造成的后果不堪設想!

2:指令打包

所以為了避免這種場景,大家都會選擇一種做法:

將.o打包形成一個庫再放進lib目錄中,再將.h都放進一個include目錄中,最后再把lib和included都放進一個目錄中(lib就是庫的縮寫,include代表存放頭文件的目錄)

?

樹形圖如下:

所以指令如下:

ar -rc libcal.c add.o sub.o //讓兩個.o形成一個名為libcal.c的靜態庫
mkdir -p mathlib/lib        //在當前目錄下創建一個mathlib/libm目錄
mkdir -p mathlib/include    //在當前目錄下創建一個mathlib/include目錄
cp ./*h mathlib/include/    //將當前目錄下的所以.h文件拷貝到mathlib/include中
cp ./*o mathlib/lib/        //將當前目錄下的libcal.c靜態庫 拷貝到mathlib//lib中

所以,這就是為什么,往往你在網上找到一個mod或者小程序,你去下載的時候,會發下你其有很多文件夾,并且文件夾里面還有文件夾,本質就是和我們的做法大同小異!

注:我們生成這個樹狀圖的過程叫做"發布"!

3:makefile打包

當然,這些所有的指令,我們都可以全部在makefile中完成!當我們以后再要生成靜態庫以及組織頭文件和庫文件時就可以一步到位了,不至于每次重新生成的時候都要敲這么多命令,這也體現了Makefile的強大。

會用到一個新的指令make output ,叫做"發布",當我們需要發布一個庫的時候,就需要使用make output 指令,其內部一般會執行的就是我們上文說的:"將.o打包形成一個庫再放進lib目錄中,再將.h都放進一個include目錄中,最后再把lib和included都放進一個目錄中!"

另外,需要先make生成靜態庫,然后才能make output進行發布!

畢竟你連庫都沒有生成,談何發布?

makefile如下所示:

無注釋版:

mylib=libcal.a
CC=gcc$(mylib): add.o sub.oar -rc -o $(mylib) $^%.o: %.c$(CC) -c $<.PHONY: clean
clean:rm -f $(mylib) ./*.o.PHONY: output
output:mkdir -p mathlib/includemkdir -p mathlib/libcp ./*.h mathlib/includecp ./*.a mathlib/lib

注釋版本:?

# 定義靜態庫名稱
mylib=libcal.a# 定義使用的編譯器
CC=gcc# 默認目標:構建靜態庫
$(mylib): add.o sub.o# 將add.o和sub.o打包成靜態庫libcal.a# -rc 表示創建新庫(r)并添加文件(c)# -o 指定輸出文件名# $^ 表示所有依賴文件(add.o sub.o)ar -rc -o $(mylib) $^# 模式規則:從.c文件生成.o文件
%.o: %.c# 編譯C源文件生成目標文件# -c 表示只編譯不鏈接# $< 表示第一個依賴文件(%.c)$(CC) -c $<# 偽目標:清理生成的文件
.PHONY: clean
clean:# 刪除靜態庫和所有.o文件rm -f $(mylib) ./*.o# 偽目標:組織輸出目錄結構
.PHONY: output
output:# 創建include目錄mkdir -p mathlib/include# 創建lib目錄mkdir -p mathlib/lib# 復制所有.h文件到include目錄cp ./*.h mathlib/include# 復制所有.a文件到lib目錄cp ./*.a mathlib/lib

makefile的效果:

目前的狀態:

make后:

mak output后:

符合預期!

4:使用

談了這么多,也終于到使用我們的庫的時候了!當然,客戶肯定使用gcc來編譯我們發布的庫!

現在用戶來了:

其第一步就是把,mathlib這個目錄拿走(類似于下載),所以指令如下:

mv mathlib/ user/   //模擬用戶的下載行為

現在用戶已經準備好了.o,也有了mathlib這個目錄,其內部有庫有頭文件

現在我們知道其肯定是需要使用gcc的三個選項的,因為庫和頭文件都不在默認搜索路徑下,頭文件也不在main.c的同級目錄下!但是我們還是模擬一下錯誤的過程吧:

①:直接gcc

解釋:報錯找不到頭文件!因為gcc默認只在當前目錄和系統路徑中找頭文件

②:僅-I

解釋:報錯add?和?sub?函數未定義!因為雖然找到了頭文件(聲明了函數),但?未鏈接函數實現的庫libcal.a?或?libcal.so),導致鏈接器找不到函數定義。

③:僅-I -L

解釋:報錯找不到庫!-lcal?會讓鏈接器查找?libcal.a?或?libcal.so,但未用?-L?指定庫路徑,鏈接器只在系統默認路徑(如?/usr/lib)中查找,而你的庫在?mathlib/lib?中。

這里涉及到一個庫的名字的獲取規則,我們的庫為libcal.a,但其實這個庫的名字為cal,因為去掉前綴lib,去掉后綴.a,因為任何庫都是lib前綴,任何靜態庫都是.a后綴 ,所以-I的時候要去掉!!

④:三個選項齊全

解釋:符合預期,未報錯!

四:動態庫的制作和使用

動態庫的制作和打包與靜態庫有些許的不同,共兩點:

①:gcc形成.o文件,需要加上 -fPIC選項

②:形成庫不再用ar指令打包,而是使用gcc的-shared選項即可打包

1:制作

先生成.o文件

gcc -fPIC -c add.c
gcc -fPIC -c sub.c
//動態庫生成.o 一定要帶-fPIC選項

2:指令打包

?對.o文件進行打包:

gcc -shared -o libcal.so add.o sub.o  //動態庫-shared選項即可打包

?

然后將.o打包形成一個庫再放進lib目錄中,再將.h都放進一個include目錄中,最后再把lib和included都放進一個目錄中

gcc -shared -o libcal.so add.o sub.o //形成動態庫
mkdir -p mathlib/include             //創建目錄
mkdir -p mathlib/lib                 //創建目錄
cp ./*.h mathlib/include             //將所有頭文件拷貝到mathlib/include中
cp ./libcal.so mathlib/lib           //將動態庫拷貝到mathlib/lib中

樹形圖如下:

和靜態庫一樣的套路,我們依舊換成makefile來進行

3:makefile打包

無注釋:

mylib=libcal.so
CC=gcc$(mylib): add.o sub.o$(CC) -shared -o $(mylib) $^%.o: %.c$(CC) -fPIC -c $<.PHONY: clean
clean:rm -rf $(mylib) ./*.o.PHONY: output
output:mkdir -p mathlib/includemkdir -p mathlib/libcp ./*.h mathlib/includecp ./*.so mathlib/lib

含注釋:

# 定義使用的編譯器
CC=gcc# 默認目標:構建動態庫
$(mylib): add.o sub.o# 將add.o和sub.o打包成動態庫libcal.so# -shared 表示生成動態鏈接庫# -o 指定輸出文件名# $^ 表示所有依賴文件(add.o sub.o)$(CC) -shared -o $(mylib) $^# 模式規則:從.c文件生成.o文件(用于動態庫需要-fPIC選項)
%.o: %.c# 編譯C源文件生成位置無關代碼(PIC)的目標文件# -fPIC 生成位置無關代碼(Position Independent Code)# -c 表示只編譯不鏈接# $< 表示第一個依賴文件(%.c)$(CC) -fPIC -c $<# 偽目標:清理生成的文件
.PHONY: clean
clean:# 刪除動態庫和所有.o文件rm -rf $(mylib) ./*.o# 偽目標:組織輸出目錄結構
.PHONY: output
output:# 創建include目錄mkdir -p mathlib/include# 創建lib目錄mkdir -p mathlib/lib# 復制所有.h文件到include目錄cp ./*.h mathlib/include# 復制所有.so文件到lib目錄cp ./*.so mathlib/lib

makefile效果:

make后:

make output后:

符合預期!

4:使用

不再演示三個選項缺失的報錯了,直接使用三個選項吧~

gcc main.c -I./mlib/include -L./mlib/lib -lcal

生成了a.out

執行a.out

解釋:竟然報錯了?!!報錯加載共享庫時出現錯誤,也就是找不到動態庫!

用ldd指令看一下鏈接的庫的信息

?

?這就是動態庫和靜態庫的區別!!

五:系統查找動態庫

a.out無法執行的原因是因為,操作系統找不到動態庫!

是的,不僅gcc需要找動態庫,OS也需要找動態庫!

Q1:那靜態庫的時候,為什么沒有報錯?OS不找靜態庫?

A1:靜態庫的特點就是已經寫在了代碼里,所以不需要找,代碼中就有,而動態庫,沒有寫在代碼里,所以,我們的三個選項只是告訴了gcc編譯器位置,而沒有告訴OS位置!

需要明白的是,gcc要找動態庫/靜態庫和頭文件,而OS只需要找動態庫!因為頭文件只有在編譯的時候才有用,在本文最開始 引入中的博客中談過,編譯的第一步預處理才需要包含頭文件,所以OS只需要找動態庫!

Q2:那OS找動態庫和gcc找動態庫的方式有什么區別?

A2:無任何區別,依舊是去庫的默認搜索路徑去找庫!因為你沒告訴OS,所以OS不知道!

Q3:第一大點中說過gcc是默認動態鏈接的,也就是是鏈接到動態庫的,那為什么我們寫代碼的時候,OS沒有報錯找不到動態庫?

A3:因為不管是什么庫,只要是標準庫,就已經被存放在了默認搜索路徑下的目錄中,OS找得到!!

Q4:那怎么告訴OS我們寫的動態庫在哪?

A4:四種方法!

四種方法,都會在使用方法前后進程ldd指令的對比,因為ldd指令可以查看os是否找到了動態庫!!!

1:拷貝到默認搜索路徑下

這種方法是最簡單的,也是最好理解的,既然你OS和gcc都要先去默認搜索路徑下找庫,那我干脆直接把庫放進默認的路徑中,但是這種方法需要sudo或者root才可以:

操作如下:

如果你是centos,則你:

sudo cp mathlib/lib/libcal.so /lib64

如果你是ubuntu,則你:

sudo cp mathlib/lib/libcal.so /lib/x86_64-linux-gnu/

因為不同版本下,OS對動態庫的默認搜索路徑不一樣~?

如何把1的操作去除:

以下每個操作后,都會去除上種方法的效果,避免影響后面的方法效果!

2:修改環境變量修改

和環境變量PATH類似,PATH是找可執行程序的路徑,而找動態庫的路徑也是可以修改對應的環境變量的

指令如下:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxxxx/mathlib/lib 
//xxx代表你的mathlib所處的路徑

所以我的指令如下:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:mathlib/lib/

效果如下:

?

注:你是給?LD_LIBRARY_PATH 這個環境變量一個路徑,所以你填寫到? mathlib/lib/ 就嘚停止,而不是mathlib/lib/libcal.so!!

?

如何去除2的效果:

unset LD_LIBRARY_PATH

因為我的這個路徑本來就沒值,所以可以直接使用unset指令清空

但是如果你的環境變量里面有值,則你需要:

export LD_LIBRARY_PATH=$(echo $LD_LIBRARY_PATH | sed 's|:mathlib/lib||g')

或者直接關閉客戶端重啟即可

3:軟鏈接

sudo ln -s /home/wtt1/lesson4/user/mathlib/lib/libcal.so /lib/x86_64-linux-gnu/libcal.so

注:

①:格式:ln -s <源文件絕對路徑> <目標位置完整路徑>

②:必須指定鏈接文件的完整路徑和名稱(不能只寫目錄路徑,精確到動態庫文件)

效果:

?

如何去除?

sudo rm /lib/x86_64-linux-gnu/libmyc.so

4:動態庫路徑配置文件

我們可以通過配置/etc/ld.so.conf.d/路徑下的文件,來讓OS找到動態庫!

/ld.so.conf.d中的ld譯為加載,so譯為動態庫,conf.d譯為配置文件的目錄

所以名字就很好理解,"加載動態庫的配置文件的目錄"

?

/etc/ld.so.conf.d/路徑下存放的全部都是以.conf為后綴的配置文件,而這些配置文件當中存放的都是路徑,系統會自動在/etc/ld.so.conf.d/路徑下找所有配置文件里面的路徑,之后就會在每個路徑下查找你所需要的庫。我們若是將自己庫文件的路徑也放到該路徑下,那么當可執行程序運行時,系統就能夠找到我們的庫文件了。

ls該目錄如下圖:

解釋:該路徑下全是.conf結尾的文件,而我們需要做的就是再創建一個.conf為后綴的文件,該文件的文件名隨便取,假設我就取wtt1.conf

所以我就在當前目錄創建一個wtt1.conf 然后將其mv到/etc/ld.so.conf.d/路徑下:

sudo mv wtt1.conf /etc/ld.so.conf.d/

?

Q:那文件的內容寫什么?

A:就寫你動態庫所處的路徑即可!

echo /home/wtt1/lesson4/user/mathlib/lib > /etc/ld.so.conf.d/wtt1.conf
//將動態庫所處的路徑 寫進/etc/ld.so.conf.d下的配置文件wtt1.conf

?此時ldd a.out 發現依舊沒有生效:

?

?因為還要執行sudo ldconfig 指令才行:

解釋:ldconfig ???指令會讓配置文件生效!

至此,4種讓OS找到動態庫的方法介紹完了!

官方下載的庫建議用第一種方法,自己實現的庫建議用第三z種方法

六:動靜態庫的優先級規則

①:如果我們同時提供動態庫和靜態庫,gcc默認使用的是動態庫?

解釋:可以在同時有t同名的動態庫和靜態庫下驗證,但是我們在第一大點中就已經驗證了,默認的gcc形成的程序,進行ldd指令或者file指令,提示消息都表明了其是動態鏈接!


②:如果我們非要靜態連接,我們必須使用static選項

③:如果我們只提供的靜態庫,那我們的可執行程序也沒辦法,即使你不指名static,其也會對該庫進行靜態連接,但是程序不一定整體是靜態連接的,因為不止鏈接這一個庫

④:如果我們只提供動態庫,默認只能動態連接,非得靜態連接,會發生連接報錯
?

七:動態庫的加載

上面六點,我們介紹了這么多關于動靜態庫的知識,所以下面大致講解一下動靜態庫加載到內存中之后是如何影響進程的內核數據結構的!

1:共享區的作用

共享區就是磁盤中的動態庫被加載到內存,然后通過頁表映射到進程的進程地址空間的位置!所以動態庫又被叫做動態庫!

2:編址

Q:我們都知道進程地址空間存放的是虛擬地址,那請問進程地址空間的虛擬地址,是誰給他的?你任何一個值,首先得被初始化才有吧?

A:程序編譯期間就會形成虛擬地址,對你沒聽錯,程序還沒成為進程,還沒占用內存,其僅僅是在編譯期間,就會讓每行代碼都有自己的虛擬地址

比如下面是一個test.s的文件,也就是遠遠還沒有形成.o的時候,其就已經有地址了:

解釋:這是在反匯編下觀察到的結果,能夠看出其的確是有很多的地址,這個行為就叫作"編址"!

編址分為兩種:絕對編址(又叫作平坦模式)和相對編址(又叫作邏輯編址)

絕對編址就是像上圖中這樣,從一個地址開始,整個程序都是遞增式的地址,又稱為“平坦模式”!上圖中的401010 --->401015--->401019--->401020 是連續的,所以是絕對編址

而相對編址,每一個代碼塊會有一個起始地址,然后該代碼塊里面的每句代碼前面不是地址,而是偏移量,所以一句代碼的地址,就是該句代碼位于的代碼塊的起始地址+偏移量

所以現在我們知道了,一個.c中的所有代碼和數據和變量等等一切東西,都會在編譯期間,就為其分配好了虛擬地址!所以此時當程序加載到內存中的時候,由于其被加載到內存中,所以其就能得到自己在內存中的物理地址,所以現在即有了物理地址,又有了虛擬地址,所以能夠通過頁表映射到進程地址空間!

而我們知道靜態庫是存在于代碼里面的,編譯的時候,靜態庫就會在直接拷貝進main.c文件中,而動態庫卻是被加載到內存,然后映射到進程地址空間的共享區的,所以這里面分別涉及到了絕對編址和相對編址!

3:動態庫的加載過程

靜態庫在編譯鏈接時,庫中所有代碼已經被拷貝到了用這個庫的main.c函數中,所以靜態庫會隨著.c文件一起被采取絕對編址的方式進行編址。當程序加載時,操作系統直接按照形成的可執行文件中絕對編址得到的虛擬地址,將靜態庫的代碼和數據段映射到進程的進程地址空間中!頁表將這些虛擬地址轉換為物理內存地址,形成映射!

動態庫不隨mian.c文件被絕對編址,其是位于磁盤中的一份獨立文件,而且動態庫通過-fPIC選項編譯,所以其在編譯的時候,在內部使用相對偏移進行編址。加載時,操作系統將動態庫的代碼映射到進程地址空間的共享區。而因為其是響度編址,所以任何方法都會有一個起始地址,方法中的語句都會有一個偏移量,所以當你的代碼中調用的庫的函數的時候,此時,當cpu讀取到這行調用庫方法的代碼時,其會得到一個起始地址和偏移量,然后跳轉到共享區對應的虛擬地址處,然后再通過該虛擬地址和頁表找到在內存中的具體實現方法,從而實現調用!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/91215.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/91215.shtml
英文地址,請注明出處:http://en.pswp.cn/web/91215.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【LY88】雙系統指南及避坑

一. Windows重裝&#xff08;前提是Windows可正常使用&#xff0c;優點是無需U盤&#xff09; 1. PE工具和系統鏡像 機械師只只提供的資源鏈接 完成微PE工具的安裝并下載了系統鏡像之后&#xff0c;&#xff08;如果要裝ubuntu的話&#xff09;需確認磁盤分區格式和引導項。前…

Ubuntu22.04.1搭建php運行環境

步驟 1: 更新你的系統 首先&#xff0c;確保你的系統是最新的。打開終端并運行以下命令&#xff1a; sudo apt update sudo apt upgrade步驟 2: 安裝Apache Web服務器 使用Apache作為你的Web服務器。運行以下命令&#xff1a; sudo apt install apache2安裝完成后&#xff0c;你…

防止飛書重復回調通知分布式鎖

## 場景銷售訂單下&#xff0c;明細25明細款&#xff0c;發起飛書審批&#xff0c;飛書設置自動審核通過&#xff0c;導致會收到兩次審核通過通知加了分布式鎖 &#xff0c;仍導致執行業務執行兩遍了String lockKey "feihsu-approvalNotify:" instanceCode; RLock …

數據結構:下三角矩陣(Lower Triangular Matrix)

目錄 什么是下三角矩陣&#xff1f; 我們要存哪些元素&#xff1f;一共幾個&#xff1f; 推導索引映射公式 核心問題&#xff1a;給定 (i,j)&#xff0c;如何計算 k&#xff1f; 什么是下三角矩陣&#xff1f; 一個 n n 的矩陣&#xff0c;如果它在主對角線以上的所有元…

力扣209:長度最小的子數組

力扣209:長度最小的子數組題目思路代碼題目 給定一個含有 n 個正整數的數組和一個正整數 target 。 找出該數組中滿足其總和大于等于 target 的長度最小的 子數組 [numsl, numsl1, …, numsr-1, numsr] &#xff0c;并返回其長度。如果不存在符合條件的子數組&#xff0c;返回…

采購管理系統哪家性價比高?

在企業數字化轉型進程中&#xff0c;采購管理系統已成為降本增效的核心工具。但面對市場上五花八門的產品&#xff0c;“性價比” 成為企業選型時的關鍵考量 —— 既要功能貼合業務需求&#xff0c;又要成本可控&#xff0c;還需兼顧實施效率與長期擴展性。以下從性價比維度解析…

輕松打造Unity小游戲AR體驗

目錄 AR會話初始化 平面追蹤與相機定位 用戶交互處理 實時渲染 Unity 小游戲宿主現已支持 AR 功能&#xff0c;本文介紹如何從零開始創建一個可以在Unity小游戲宿主上運行的AR小游戲&#xff0c;歡迎大家試用&#xff01; 想為你的小游戲注入虛實交融的魔力嗎&#xff1f;…

IFCVF驅動+vhost-vfio提高虛擬機網絡性能

??IFCVF (Intel FPGA Virtual Function)?? 是 Intel 為其基于 FPGA 的智能網卡開發的 ??SR-IOV 虛擬功能驅動??,屬于 ??PF4 (Physical Function 4)?? 架構的一部分。它是專為高性能網絡虛擬化場景設計的硬件加速解決方案。 云計算智能網卡(soc)或DPU場景下,IFC…

Hook捕獲并攔截文件創建行為

需要用到minhook 先編譯DLL #include <Windows.h> #include <string> #include <TlHelp32.h> #include <Shlwapi.h>#include "MinHook.h" // 自動選擇正確的MinHook庫 #pragma comment(lib, "Shlwapi.lib") #if defined(_M_X64) …

圖像平滑處理

圖像平滑處理四種常用方式1. 均值濾波 (cv2.blur())2. 高斯濾波 (cv2.GaussianBlur())3. 中值濾波 (cv2.medianBlur())4、雙邊濾波 (cv2.bilateralFilter())總結存圖時遇到一個中文版亂碼問題四種常用方式 平滑處理&#xff08;也稱為模糊處理&#xff09;&#xff0c;用于減少…

fortigate的waf功能

在系統管理----可見功能----web應用防火墻打開waf功能Web 應用程序防火墻 &#xff08;WAF&#xff09; 配置文件可以檢測和阻止已知的 Web 應用程序攻擊。您可以將 WAF 配置文件配置為使用簽名和約束來檢查 Web 流量。您還可以強制實施 HTTP 方法策略&#xff0c;該策略控制與…

AI Compass前沿速覽:可靈創意工坊、字節Coze StudioCoze Loop、通義萬相2.2 、智譜GLM-4.5、騰訊混元3D世界模型開源

AI Compass前沿速覽&#xff1a;可靈創意工坊、字節Coze Studio&Coze Loop、通義萬相2.2 、智譜GLM-4.5、騰訊混元3D世界模型開源 AI-Compass 致力于構建最全面、最實用、最前沿的AI技術學習和實踐生態&#xff0c;通過六大核心模塊的系統化組織&#xff0c;為不同層次的學…

SpringCloud之Gateway

SpringCloud之Gateway 官網地址&#xff1a; https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories 1. 什么是gateway Spring Cloud Gateway 是Spring Cloud官方推出的第二代網關框架&#xff0c;定位于取代 Net…

關于獲取某目錄及子目錄下所有文件且不包含隱藏文件

最近比較忙&#xff0c;很少寫blog了&#xff01;&#xff01;&#xff01;關于獲取目錄及子目錄下所有文件是常遇到的功能&#xff0c;一般通過遞歸遍歷實現。而生產場景中&#xff0c;一般是遍歷nas上的目錄&#xff0c;在nas上利用File.listFiles(),在linux系統上無法獲取含…

docker可視化管理工具lazydocker

Lazydocker 是一個用 Go 語言編寫的命令行 Docker 管理工具。它提供了一個簡潔、直觀的終端界面&#xff0c;支持鍵盤和鼠標操作&#xff0c;可通過方向鍵與快捷鍵實時查看和管理容器、鏡像、網絡等資源&#xff0c;大幅簡化了原本復雜的命令行操作&#xff0c;提升操作效率。2…

少林寺用什么數據庫?

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 作者&#xff1a;IT邦德 中國DBA聯盟(ACDU)成員&#xff0c;15年DBA工作經驗 Oracle、PostgreSQL ACE CSDN博客專家及B站知名UP主&#xff0c;全網粉絲15萬 擅長主流Oracle、MySQL、PG、高斯及…

C語言---萬能指針(void *)、查找子串(strncmp函數的應用)多維數組(一維數組指針、二維數組指針)、返回指針值函數、關鍵字(const)

一、字符串與指針用字符指針指向一個字符串&#xff0c;可以不定義字符數組&#xff0c;而定義字符指針。用字符指針指向字符串中的字符。不能使用指針去改變不能修改的空間。eg1. 運用指針將 src 的內容拷貝到 dest 中去void Strcpy(char *dest, char *src) {while(*src ! \0)…

Keepalived 實戰

一、高可用集群基礎核心概念與指標集群類型&#xff1a;LB&#xff08;負載均衡&#xff09;&#xff1a;如 LVS、HAProxy、Nginx&#xff0c;提升吞吐量&#xff1b;HA&#xff08;高可用&#xff09;&#xff1a;保障核心服務&#xff08;數據庫、Redis&#xff09;連續性&am…

窗口函數替代子查詢的復雜查詢簡化技巧

窗口函數通過單次掃描完成分析計算&#xff0c;能大幅簡化子查詢結構并提升性能&#xff0c;尤其在排名、累計計算等場景?15。以下是核心優化技巧&#xff1a;一、排名場景替代方案?部門工資排名?傳統子查詢需自連接和聚合計數&#xff1a;sqlSELECT e1.name, e1.salary, (S…

深度學習:預訓練和warm up的區別

“預訓練&#xff08;Pre-training&#xff09;”和“Warm-up&#xff08;預熱&#xff09;”是深度學習中常見的兩個訓練策略&#xff0c;它們雖然都在訓練初期起作用&#xff0c;但本質和目的完全不同。一、預訓練&#xff08;Pre-training&#xff09;1. 定義預訓練是指&…