環(huán)境:centos7.6,騰訊云服務(wù)器 Linux文章都放在了專欄:【?Linux?】歡迎支持訂閱???
相關(guān)文章:
【Linux】動靜態(tài)庫以及動靜態(tài)鏈接
【Linux】基礎(chǔ)IO_文件系統(tǒng)
軟硬鏈接
軟鏈接
我們知道,每一個(gè)文件都會有自己的inode編號,我們可以通過如下指令來查看:
ls -l -i
?我們發(fā)現(xiàn),每一個(gè)不同文件的inode編號都不相同,所以inode可以說是用來標(biāo)識文件的標(biāo)識符。接下來,我們通過下面指令來給mysoft文件,創(chuàng)建軟鏈接:
ln -s mysoft mysoft-s
?我們發(fā)現(xiàn),創(chuàng)建出來的軟鏈接文件的inode編號與源文件并不相同,并且,軟鏈接文件的大小遠(yuǎn)遠(yuǎn)小于源文件的大小。實(shí)際上,軟鏈接又稱為符號鏈接,軟鏈接文件是一個(gè)獨(dú)立的文件,有自己的inode屬性以及內(nèi)容,軟鏈接文件的內(nèi)容就是只包含了源文件的路徑名稱。因此大小要比源文件要小很多。這就類似于Windows下創(chuàng)建的快捷方式文件。
當(dāng)然,假如我們將源文件刪除或者改變源文件路徑后,那么軟鏈接文件也就運(yùn)行不了,因?yàn)檐涙溄游募?nèi)僅僅只是源文件所在的路徑,當(dāng)源文件不在時(shí),軟鏈接文件也就“失效了”。如下所示:
?硬鏈接
接下來我們來看硬鏈接,我們可以通過下面命令給mylink文件創(chuàng)建硬鏈接:
ln mylink mylink-h
我們發(fā)現(xiàn),創(chuàng)建出來的硬鏈接文件不管是大小,還是inode編號都與源文件相同,并且那個(gè)數(shù)字也從1變成了2。可以看出來,硬鏈接代表的意義就是給源文件起一個(gè)“別名”,也就是說,雖然它們名字不同,但代表的都是同一個(gè)文件,因?yàn)樗鼈兊膇node編號相同!。而這個(gè)數(shù)字2,就是用來統(tǒng)計(jì)一個(gè)文件究竟有幾個(gè)“別名”。因此這里變成了2。
當(dāng)然,假如此時(shí)我們將源文件的路徑給更改,并不會發(fā)生什么影響,但是假如我們將源文件給刪除。我們會發(fā)現(xiàn),那個(gè)數(shù)字就會由2變成1,但依然可以運(yùn)行。
接下來我們看一個(gè)現(xiàn)象:為什么目錄文件硬鏈接數(shù)為2?
?答案是很簡單,因?yàn)槊恳粋€(gè)目錄文件,即使是個(gè)空目錄,里面也一定有如下內(nèi)容:當(dāng)前路徑文件.和上級路徑文件..。
?當(dāng)然,假如我們在B目錄文件下再建立一個(gè)新的路徑文件,那么B的硬鏈接數(shù)就會由2變成3,因?yàn)樾碌穆窂轿募露紩幸粋€(gè)..文件,..標(biāo)識上級路徑,也就是B,因此B的硬鏈接數(shù)會由2變3。
這里有一點(diǎn)需要注意:我們可以給普通文件創(chuàng)建硬鏈接,但不能給目錄文件創(chuàng)建硬鏈接,因?yàn)榧偃缒芙o目錄文件建立硬鏈接,就容易發(fā)生環(huán)路路徑問題。
?軟硬鏈接的區(qū)別
- 軟鏈接又稱為符號鏈接,是一個(gè)獨(dú)立的文件,有單獨(dú)的inode編號,該文件的內(nèi)容為目標(biāo)文件的路徑。
- 硬鏈接是將不同的文件名關(guān)聯(lián)到同一個(gè)inode節(jié)點(diǎn),名字不同,但都是指同一個(gè)文件。
- 軟鏈接可以給目錄創(chuàng)建,但硬鏈接不可以給目錄創(chuàng)建
- 刪除原目標(biāo)文件后,軟鏈接文件會收到影響,會“失效”,但硬鏈接文件不受影響,依然可以正常運(yùn)行,僅僅只是硬鏈接數(shù)-1。
- 硬鏈接的文件屬性類型與原文件保持一致,而軟鏈接文件的屬性類型為l,l表示鏈接文件
- 軟鏈接的大小很小,硬鏈接的大小與原目標(biāo)文件一致,因?yàn)橛叉溄游募旧砭褪窃繕?biāo)文件的“別名”。
動靜態(tài)庫
什么是庫文件?
我們在編寫C/C++代碼時(shí),實(shí)際上一直都在用庫(c/c++標(biāo)準(zhǔn)庫),在編寫代碼時(shí),有很多庫函數(shù)諸如printf等,我們?yōu)槭裁茨苤苯幽脕碛媚??是因?yàn)槲覀儼烁髯詫?yīng)的頭文件,而頭文件的內(nèi)容包含了該函數(shù)的聲明,具體的實(shí)現(xiàn)方法則在庫文件中,在鏈接階段,我們經(jīng)過編譯后的.o文件會與庫文件進(jìn)行合并,最終形成可執(zhí)行程序。
因此,可以這么來說:庫文件中提供函數(shù)的實(shí)現(xiàn)方法,而頭文件中提供函數(shù)說明。兩者配套使用。實(shí)際上,庫其實(shí)就是大量方法文件形成的.o文件的集合。
為什么要存在庫?
庫的存在就是為了提高開發(fā)效率,舉個(gè)例子,假如沒有c/c++標(biāo)準(zhǔn)庫,我們在寫代碼時(shí)就要手動將printf、cout等高頻函數(shù)的實(shí)現(xiàn)方法進(jìn)行編寫,這樣就大大減少了我們的開發(fā)效率。
而且假如在日常開發(fā)中,假設(shè)別人想要使用我們實(shí)現(xiàn)的一些接口,但是我們又不想讓別人看到我們是如何實(shí)現(xiàn)的,此時(shí)我們就可以將接口的實(shí)現(xiàn)打包成一個(gè)庫,然后直接將庫文件和對應(yīng)頭文件發(fā)送給對方即可。
庫又分為動態(tài)庫和靜態(tài)庫,兩者的優(yōu)缺點(diǎn)在之前的文章已經(jīng)詳細(xì)講解<<點(diǎn)擊跳轉(zhuǎn)>>,這里主要講如何庫的使用和原理。
如何制作和使用第三方庫
第一方庫:語言提供的庫(如c/c++標(biāo)準(zhǔn)庫)
第二方庫:操作系統(tǒng)提供
第三方庫:other提供,比如我們接下來自己制作的動靜態(tài)庫
靜態(tài)庫的打包
靜態(tài)庫的打包主要分為兩個(gè)步驟:
- 將存放方法的源文件進(jìn)行編譯,編譯后(含預(yù)處理--編譯--匯編)生成.o為后綴的可重定位二進(jìn)制目標(biāo)文件。(gcc -c)
- 將所有的.o文件使用ar -rc指令,進(jìn)行打包形成靜態(tài)庫。
- 將靜態(tài)庫與頭文件壓縮后發(fā)送給他人即可供他人使用
這里我簡單舉個(gè)例子:
假如我自己寫了一個(gè)Add和Sub接口的實(shí)現(xiàn),然后其他人想直接用我們的接口,此時(shí)我們想在不將方法的具體實(shí)現(xiàn)暴露出來,僅僅是將接口的功能給他人使用,此時(shí)我們就可以打包成庫,發(fā)送給別人即可。如下圖:
第一步:使用g++ -c的指令,將方法實(shí)現(xiàn)的源文件進(jìn)行編譯后生成.o結(jié)尾的可重定位二進(jìn)制文件:
??第二步:使用ar -rc 指令將所有的.o文件進(jìn)行打包
注意:ar是gnu歸檔工具,通常用指令ar -rc來進(jìn)行靜態(tài)庫打包。而ar -tv指令則可以查看靜態(tài)庫的內(nèi)容。如下:
?接下來,我們再將所有的頭文件都放在同一個(gè)文件下,如下所示:
第三步:使用tar指令將庫文件與頭文件進(jìn)行壓縮,然后發(fā)送給其他人 使用即可
靜態(tài)庫的使用
?此時(shí)我們切換身份,我們作為otherPeople,我們想要使用這個(gè)靜態(tài)庫,該如何使用呢?
第三方庫的使用規(guī)則
首先,任何第三方庫的使用,必須在編譯時(shí)要標(biāo)注三個(gè)要素:庫所在的路徑、對應(yīng)頭文件的路徑、要鏈接的庫名(庫名需要去掉前綴與后綴)。
第一種方式使用靜態(tài)庫:編譯時(shí)手動指定
gcc/g++編譯選項(xiàng) | 含義 |
-L | 指定庫所在的路徑 |
-I(大寫i) | 指定頭文件所在路徑 |
-l(小寫L) | 指定庫名稱(去掉前后綴) |
如下,假如我要使用這個(gè)靜態(tài)庫,我先將這個(gè)壓縮包解壓:
?接下來我們進(jìn)行g(shù)++編譯,這里編譯時(shí)我們手動指定所需要的庫名(-l)、庫路徑(-L)、頭文件路徑(-I)。
對于靜態(tài)庫的使用,還有第二種方法如下:
第二種方式使用靜態(tài)庫:將頭文件以及庫文件安裝在系統(tǒng)目錄
由于gcc/g++在編譯時(shí),會默認(rèn)去系統(tǒng)目錄搜索,進(jìn)行路徑匹配,這也是為什么我們平常用c/c++標(biāo)準(zhǔn)庫時(shí),編譯時(shí)不需要再指定庫路徑和頭文件路徑。
庫所在系統(tǒng)路徑:/lib64
頭文件所在系統(tǒng)路徑:/usr/include
?這里需要注意的是,一般我們不要輕易修改系統(tǒng)的環(huán)境,因此我們用完后要手動刪除,否則會一直存在。
動態(tài)庫的打包
上面講了靜態(tài)庫的打包和使用,接下來將動態(tài)庫的打包和使用,以及動態(tài)庫鏈接的原理。
動態(tài)庫的打包分為以下幾個(gè)步驟:
- 將存放方法的源文件進(jìn)行編譯,編譯后(含預(yù)處理--編譯--匯編)生成以.o為后綴的可重定位二進(jìn)制目標(biāo)文件。同時(shí)在編譯時(shí)生成與位置無關(guān)碼。(gcc/g++ -c -fPIC)
- 直接gcc/g++對所有的.o文件進(jìn)行編譯,同時(shí)加上-shared選項(xiàng),打包成動態(tài)庫即可。(gcc/g++ -shared)
- 將動態(tài)庫與頭文件壓縮后發(fā)送給他人即可供他人使用
以上靜態(tài)庫例子打包成動態(tài)庫,步驟如下所示:
?緊接著我們可以將動態(tài)庫與頭文件進(jìn)行壓縮,將壓縮包給other用戶,供他人使用。
?這里有一點(diǎn)需要注意,就是我們一般會把頭文件,單獨(dú)放在一個(gè)目錄,庫文件單獨(dú)放在一個(gè)目錄。(上面靜態(tài)庫的例子忘記了,這里說一下。)
動態(tài)庫的使用
接下來我們已other的身份,進(jìn)行使用動態(tài)庫,我們先將壓縮包進(jìn)行解壓,然后進(jìn)行編譯,編譯時(shí)指定頭文件、庫文件的路徑,以及庫名。
?雖然可以正常編譯,但是此時(shí)我們運(yùn)行可執(zhí)行程序,就會出現(xiàn)如下報(bào)錯(cuò):
這是因?yàn)槲覀冊诰幾g時(shí),僅僅只是告訴了編譯器,但是OS并不知曉,OS只會在系統(tǒng)路徑,以及當(dāng)前所在路徑下進(jìn)行查找頭/庫文件。因此此時(shí)會報(bào)錯(cuò)。(靜態(tài)鏈接并不會,因?yàn)樯傻目蓤?zhí)行程序的運(yùn)行,不會依賴庫),這時(shí)常用的解決方法有如下幾種:
?1、將庫文件拷貝到系統(tǒng)路徑
此時(shí)我們假如將我們的第三方動態(tài)庫,拷貝到系統(tǒng)路徑/lib64下,即可正常運(yùn)行。如下:
?需要注意的是,我們一般不會對系統(tǒng)環(huán)境進(jìn)行更改,用完后要進(jìn)行刪除,否則的話,下一次重新登陸,系統(tǒng)路徑下還是會存在我們自己的第三方庫。
2、將庫路徑導(dǎo)入環(huán)境變量LD_LIBRARY_PATH中
用export指令,將庫路徑(絕對路徑)導(dǎo)入環(huán)境變量LD_LIBRARY_PATH中,如下:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/qidunyan/linux-exercise/test_23_06/test_23_0607/otherPeople/mylib/
此時(shí)程序依然可以正常運(yùn)行
?該方法的好處就是用完后,我們不需要對該環(huán)境變量進(jìn)行刪除庫的路徑,因?yàn)橄麓沃匦碌顷憰r(shí),該環(huán)境變量會被更新。
3、對系統(tǒng)配置文件/etc/ld.so.conf.d進(jìn)行更改
系統(tǒng)配置文件/etc/ld.so.conf.d中存放的都是以.conf為后綴的文件,該文件內(nèi)存放的是路徑。我們只需要將動態(tài)庫的絕對路徑,放在一個(gè)以.conf為后綴的文件中,再將該文件拷貝到系統(tǒng)配置文件內(nèi)即可。如下:
?但是此時(shí)運(yùn)行的話,依然會顯示失敗,因?yàn)槲覀儧]有對配置文件進(jìn)行更新,我們只需要輸入指令:ldconfig,進(jìn)行更新配置文件即可。
?當(dāng)然,我們?yōu)榱瞬晃廴鞠到y(tǒng)環(huán)境,使用完后也要記得手動刪除,否則會一直存在。
動靜態(tài)庫鏈接的原理
為什么靜態(tài)鏈接生成的可執(zhí)行程序,不會依賴庫文件呢?因?yàn)樵诰幾g階段會將庫中方法的代碼加載到可執(zhí)行程序中,這樣就會出現(xiàn)一個(gè)情況,假如同一個(gè)方法比如printf,被調(diào)用了多次,這也就會導(dǎo)致printf實(shí)現(xiàn)的代碼,被重復(fù)復(fù)制了多次,出現(xiàn)大量冗余重復(fù)的代碼,這也就是為什么靜態(tài)鏈接生成的可執(zhí)行程序體積大小非常大的原因。
而動態(tài)鏈接則不是這樣,程序在鏈接動態(tài)庫時(shí),會通過庫的起始地址+偏移量,來找到函數(shù)方法所在的位置,而這個(gè)偏移量,就是我們生成的與位置無關(guān)碼。其實(shí)就是0 1 2 3 4...這樣的偏移量,也就是說,只要知道庫的起始地址,就能根據(jù)0 1 2 3...這樣的偏移量,來找到各自的方法。為什么叫與位置無關(guān)碼呢?因?yàn)閹毂挥成涞降刂房臻g的地址是不確定的,但是偏移量是固定的,這樣不管庫被映射到哪個(gè)地址,通過偏移量都可以找到函數(shù)所在的位置。(舉個(gè)例子,假如我對你說,我距離你10米遠(yuǎn),那么不管你的位置在哪里,只需要從你的位置+10米,就可以找到我,這個(gè)10米,就類似位置無關(guān)碼)
而在程序運(yùn)行時(shí),動態(tài)庫會被加載到物理內(nèi)存,同時(shí)會通過頁表映射到進(jìn)程對應(yīng)的地址空間中的共享區(qū)。此時(shí)動態(tài)庫的地址也就有了。且同一個(gè)方法,它的庫地址+偏移量相同,所以代碼只需要存在一份即可,避免了代碼冗余。同時(shí)假如存在多個(gè)進(jìn)程同時(shí)運(yùn)行且使用同一個(gè)庫,那么動態(tài)庫也只需要在內(nèi)存中加載一份,然后映射到各自的共享區(qū),通過庫地址+偏移量就可以跳轉(zhuǎn)到方法的實(shí)現(xiàn)。大大節(jié)省了空間的使用。
補(bǔ)充
云服務(wù)器默認(rèn)只存在動態(tài)庫,因此我們?nèi)粝胧褂肅/C++靜態(tài)庫,需手動安裝
安裝C/C++靜態(tài)庫
sudo yum install -y glibc-static
sudo yum install -y libstdc++-static
另外,我們需要知道以下幾點(diǎn):
gcc/g++默認(rèn)采用動態(tài)鏈接,但是假如只存在靜態(tài)庫,則gcc/g++只會進(jìn)行靜態(tài)鏈接,同樣,只存在動態(tài)庫,也只能進(jìn)行動態(tài)鏈接(即使我們加上 -static)。
而若動靜態(tài)庫同時(shí)存在,則gcc/g++會默認(rèn)進(jìn)行動態(tài)鏈接。也可以手動指定進(jìn)行靜態(tài)鏈接(-static)
end.文章來源:http://www.zghlxwxcb.cn/news/detail-495528.html
生活原本沉悶,但跑起來就會有風(fēng)!??文章來源地址http://www.zghlxwxcb.cn/news/detail-495528.html
到了這里,關(guān)于【Linux】軟硬鏈接與動靜態(tài)庫的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!