?????爆笑教程????《看表情包學(xué)Linux》???猛戳訂閱????
?? 寫在前面:上一章我們講解了 inode,為文件系統(tǒng)收了尾,這幾章我們充分地講解完了文件系統(tǒng)的知識(shí)點(diǎn),現(xiàn)在我們開(kāi)始開(kāi)始學(xué)習(xí)軟硬鏈接了。如果沒(méi)有文件系統(tǒng)的鋪墊,想直接理解軟硬鏈接難免有些困難。但我們講完了文件系統(tǒng)再去理解軟硬鏈接,你就會(huì)發(fā)現(xiàn)沒(méi)有那么難,因?yàn)槲覀兪菑牡讓娱_(kāi)始,向上去學(xué)習(xí)的!讓我們開(kāi)始吧。
?? 本章目錄:
Ⅰ. 軟硬鏈接
0x00?Linux 下的快捷方式:軟鏈接
?0x01 創(chuàng)建軟鏈接
0x02 創(chuàng)建硬鏈接
0x03 軟硬鏈接的刪除
0x04 硬連接數(shù)
0x05 思考一些問(wèn)題
Ⅱ. 動(dòng)靜態(tài)庫(kù)
0x00 引入:什么是動(dòng)靜態(tài)庫(kù)?
0x01 動(dòng)態(tài)鏈接(Dynamic Linking)
0x02 為什么需要靜態(tài)庫(kù)?
0x03 生成靜態(tài)庫(kù):ar -rc
0x03 生成動(dòng)態(tài)庫(kù)
0x04 同時(shí)生成動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)
0x05 使用靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
Ⅰ. 軟硬鏈接
0x00?Linux 下的快捷方式:軟鏈接
上一章我們介紹完了 inode ,我們?cè)倩仡櫼幌?strong>?元數(shù)據(jù):
七列,分別是模式、軟硬連接數(shù)、文件所有者、組、大小、最后修改時(shí)間和文件名。
上圖中,紅色圈出的就是 軟硬連接數(shù) 了,我們剛才說(shuō)了,可以使用 stat 文件名查看更多:
我們可以看到,我們的 mytest.c 文件的軟硬連接數(shù)是 1。
?? 軟硬鏈接的區(qū)別:
- 軟鏈接:是一個(gè)獨(dú)立文件,有自己獨(dú)立的 inode 和 inode 編號(hào)。
- 硬鏈接:不是一個(gè)獨(dú)立的文件,它和目標(biāo)文件使用的是同一個(gè) inode。硬鏈接就是單純的在 Linux 指定的目錄下,給指定的文件新增 文件名 和 inode 編號(hào)的映射關(guān)系!
我們可以通過(guò)如下命令,創(chuàng)建一個(gè)文件的軟硬鏈接:
$ ln -s 文件名 鏈接文件名 # 創(chuàng)建軟連接
$ ln 文件名 鏈接文件名 # 創(chuàng)建硬鏈接
(下面我們先來(lái)講軟連接的創(chuàng)建,再講硬鏈接的創(chuàng)建)
?0x01 創(chuàng)建軟鏈接
我們創(chuàng)建一個(gè)軟連接,可以使用下面的指令:
$ ln -s 文件名 鏈接文件名 # 創(chuàng)建軟連接
比如我們創(chuàng)建一個(gè) my.txt 文件,我們像創(chuàng)建一個(gè) my.txt 文件的軟鏈接,我們可以:
這就是軟連接,my.txt 和 my.txt.soft 的 inode 是不同的:
下面我們來(lái)舉一個(gè)實(shí)際的例子來(lái)體驗(yàn)軟鏈接有什么實(shí)際的用途:
?? 代碼演示:vim mytest.c
#include <stdio.h>
int main(void) {
printf("hello, soft link...\n");
printf("hello, soft link...\n");
printf("hello, soft link...\n");
printf("hello, soft link...\n");
printf("hello, soft link...\n");
printf("hello, soft link...\n");
return 0;
}
?? 運(yùn)行結(jié)果如下:
程序正常運(yùn)行,這里我們?cè)?d1/d2/d3 下直接 ./mytest.exe 就可以運(yùn)行。
但是,如果我們?nèi)绻朐谕饷孢\(yùn)行這個(gè)程序就會(huì)很累,因?yàn)樗穆窂接悬c(diǎn)深:
太麻煩了,所以這里我們就可以給它建立一個(gè)軟連接,解脫雙手:
$ ln -s ./d1/d2/d3/mytest.exe my.exe
這是不是有點(diǎn)像 Windows 下的 快捷方式?沒(méi)錯(cuò)!
" 軟鏈接就是 Linux 下的快捷方式 "
上面我們演示的是讓軟鏈接鏈接一個(gè)可執(zhí)行程序,未來(lái)我們可以用它來(lái)鏈接頭文件、庫(kù)文件,動(dòng)靜態(tài)庫(kù),這樣就可以不需要讓我們?nèi)哂嗟脑谌ツ承┑胤秸疫@些庫(kù)了。
0x02 創(chuàng)建硬鏈接
對(duì)我們來(lái)說(shuō),硬鏈接是什么呢?硬鏈接其實(shí)非常簡(jiǎn)單!我們創(chuàng)建一個(gè)硬鏈接:
$ ln 文件名 鏈接文件名 # 創(chuàng)建硬鏈接
my.txt 和 my.txt.hard 映射的是同一個(gè) inode:
硬鏈接就是單純的在 Linux 指定的目錄下,給指定的文件新增文件名和 inode 編號(hào)的映射關(guān)系!
0x03 軟硬鏈接的刪除
刪除的話可以直接 rm,但是我們還是建議使用專門的 取消鏈接 的指令:unlink
$ unlink 鏈接文件名? ?# 取消鏈接
舉個(gè)例子,我們把剛才創(chuàng)建的軟鏈接和硬鏈接用 unlink 把它們揚(yáng)了:
?這個(gè) unlink 就是用來(lái)取消鏈接的,但它也可以用來(lái)刪文件。
0x04 硬連接數(shù)
我們先打道回府,重新創(chuàng)建一個(gè)硬鏈接,然后我們重點(diǎn)觀察一下下面的 "數(shù)字":
我們可以再多建立幾個(gè)硬鏈接,你可以看到這個(gè)數(shù)字的變化:
? 什么是硬鏈接數(shù)?
你看這個(gè) inode 編號(hào),是不是有點(diǎn)像指針的概念?
硬鏈接本質(zhì)就是該文件 inode 屬性中的一個(gè)計(jì)數(shù)器 count。用來(lái)標(biāo)識(shí)就幾個(gè)文件名和我的 inode 建立了映射關(guān)系。簡(jiǎn)而言之,就是有自己文件名指向我的 inode (文件本身) 。
" 軟鏈接就是 Linux 下的快捷方式 "
既然是一個(gè)獨(dú)立的文件,inode 是獨(dú)立的,軟連接的文件內(nèi)容保存的是指向文件的所在路徑。
0x05 思考一些問(wèn)題
? 思考:為什么創(chuàng)建普通文件,硬鏈接數(shù)默認(rèn)是 1 ?
因?yàn)?普通文件的文件名本身就和自己的 inode 具有映射關(guān)系,而且只有一個(gè)!
所以默認(rèn)的硬鏈接數(shù)為 1。那為什么目錄是 2 呢 ?
我們知道,任意一個(gè)目錄一定存在一個(gè)點(diǎn)或兩個(gè)點(diǎn): .? ?..
那么 ./ 為什么表示的是當(dāng)前路徑呢?因?yàn)?. 表示的就是 mydir,當(dāng)前所處的路徑!? ?
默認(rèn)一個(gè)空目錄創(chuàng)建一個(gè) 自己的名字 和 一個(gè)點(diǎn),所以兩個(gè)文件名指向它,所以是 2。
那么 .. 又是什么呢?.. 指向的是上級(jí)路徑!
這就是為什么我們 cd .. 可以回到上級(jí)目錄的原因,因?yàn)樗梢灾赶蛏霞?jí)目錄。
Ⅱ. 動(dòng)靜態(tài)庫(kù)(Dynamic Static library)
0x00 引入:什么是動(dòng)靜態(tài)庫(kù)?
動(dòng)靜態(tài)庫(kù) —— 即 動(dòng)態(tài)庫(kù) (Dynamic Library) 與 靜態(tài)庫(kù) (Static Library) 。
下面我們來(lái)分別介紹一下這兩種庫(kù):
①?動(dòng)態(tài)庫(kù)?.so:程序在運(yùn)行的時(shí)才去鏈接動(dòng)態(tài)庫(kù)的代碼,多個(gè)程序共享使用庫(kù)的代碼。
② 靜態(tài)庫(kù) .a:程序在編譯鏈接的時(shí)候把庫(kù)的代碼鏈接到可執(zhí)行文件中。程序運(yùn)行的時(shí)候?qū)⒉辉傩枰o態(tài)庫(kù)
0x01 動(dòng)態(tài)鏈接(Dynamic Linking)
一個(gè)與動(dòng)態(tài)庫(kù)鏈接的可執(zhí)行文件僅僅包含它用到的函數(shù)入口地址的一個(gè)表,而不是外部函數(shù)所在目標(biāo)文件的整個(gè)機(jī)器碼。在可執(zhí)行文件開(kāi)始運(yùn)行前,外部函數(shù)的機(jī)器碼由操作系統(tǒng)從磁盤上的該動(dòng)態(tài)庫(kù)中復(fù)制到內(nèi)存中,這個(gè)過(guò)程稱為 動(dòng)態(tài)鏈接 (dynamic linking) 。
動(dòng)態(tài)庫(kù)可以在多個(gè)程序間共享,所以 動(dòng)態(tài)鏈接使得可執(zhí)行文件更小,節(jié)省了磁盤空間。 操作系統(tǒng)采用虛擬內(nèi)存 (VM) 機(jī)制允許物理內(nèi)存中的一份動(dòng)態(tài)庫(kù)被要用到該庫(kù)的所有進(jìn)程共用,節(jié)省了內(nèi)存和磁盤空間。
測(cè)試程序:
/add.h/
#ifndef __ADD_H__
#define __ADD_H__
int add(int a, int b);
#endif // __ADD_H__
/add.c/
#include "add.h"
int add(int a, int b)
{
return a + b;
}
/sub.h/
#ifndef __SUB_H__
#define __SUB_H__
int sub(int a, int b);
#endif // __SUB_H__
/add.c/
#include "add.h"
int sub(int a, int b)
{
return a - b;
}
///main.c
#include <stdio.h>
#include "add.h"
#include "sub.h"
int main(void)
{
int a = 10;
int b = 20;
printf("add(10, 20)=%d\n", a, b, add(a, b));
a = 100;
b = 20;
printf("sub(%d,%d)=%d\n", a, b, sub(a, b));
}
0x02 為什么需要靜態(tài)庫(kù)?
我們先站在設(shè)計(jì)庫(kù)的工程師的角度,學(xué)如何形成靜態(tài)庫(kù)。
我們直接實(shí)操式地講解,下面我們會(huì)寫一段簡(jiǎn)單的、包含頭文件和源文件的代碼。
?? 代碼演示:mymath.h
#include <stdio.h>
#include <assert.h>
/* [from, to] -> 累加 -> result -> return */
extern int addToVal(int form, int to);
?? 代碼演示:mymath.c
#include "mymath.h"
int addToVal(int from, int to) {
assert(from <= to);
int result = 9;
int i = 0;
for (i = from; i <= to; i++) {
result += i;
}
return result;
}
這是一個(gè)再簡(jiǎn)單不過(guò)的實(shí)現(xiàn)累加功能的代碼,現(xiàn)在,我們想把這個(gè)小功能制作成庫(kù)給人用。
? 思考:庫(kù)里面需不需要 main 函數(shù)?
我們可不敢在庫(kù)里帶 main 函數(shù),用戶到時(shí)候會(huì)寫 main 函數(shù)的,帶了編譯出現(xiàn)沖突就不好了。
所以庫(kù)里面不要帶 main 函數(shù),也不需要帶 main 函數(shù)。
?? 代碼演示:test.cpp
#include "mymath.h"
int main(void)
{
int from = 10;
int to = 20;
int result = addToVal(from, to);
printf("result = %d\n", result);
}
?? 編譯結(jié)果如下:
下面我們來(lái)形成一個(gè)靜態(tài)庫(kù)。我們先用命令行來(lái)寫一下。
目前只有 2 個(gè),不夠豐富,所以我們?cè)偬砑右粋€(gè) myprint 功能,打印時(shí)間。
?? 代碼演示:myprintf.h
#include <stdio.h>
#include <time.h>
extern void Print(const char* msg);
?? 代碼演示:myprintf.c
#include "myprint.h"
void Print(const char* msg) {
printf("%s : %lld\n", msg, (long long)time(NULL));
}
現(xiàn)在我們有兩組方法,一個(gè)是累加一個(gè)是打印時(shí)間,我們想把它們打包成庫(kù)。
首先,我們將所有的 .c 文件翻譯成 .o 文件:
mymath.o : mymath.c
gcc -c mymath.c -o mymath.o
myprint.o : myprint.c
gcc -c myprint.c -o myprint.o
.PHONY : clean
clean:
rm -f *.o
我們知道,鏈接 就是把所有的 .o 鏈接形成一個(gè)可執(zhí)行程序。
? 思考:如果我把所有的 .o 給別人,別人能鏈接使用嗎?可以!
只需要把程序變?yōu)?.o 就可以讓被人鏈接用起來(lái)了,但是我們 .o 如果很多這會(huì)帶來(lái)不方便。
所以我們給這些 .o 做一個(gè) "打包",這就是靜態(tài)庫(kù)的作用。
0x03 生成靜態(tài)庫(kù):ar -rc
下面我來(lái)學(xué)習(xí)如何形成靜態(tài)庫(kù):
$ ar -rc [靜態(tài)庫(kù)] [.o]
ar 是 gnu 歸檔工具,rc 的意思是 replace and create (把原來(lái)打包的 .o 替換下)。
庫(kù)的命名以 lib 開(kāi)頭,靜態(tài)庫(kù)以 .a 結(jié)尾,我們寫進(jìn) Makefile:
libmymath.a : mymath.o myprint.o
ar -rc libmymath.a mymath.o myprint.o
mymath.o : mymath.c
gcc -c mymath.c -o mymath.o
myprint.o : myprint.c
gcc -c myprint.c -o myprint.o
.PHONY : clean
clean:
rm -f *.o *.a # 刪除.a文件
此時(shí)我們就有了靜態(tài)庫(kù),所謂了靜態(tài)庫(kù)就是曾經(jīng)的源文件最終將它翻譯成 .o 打包起來(lái)的東西而已。而別人用我們的庫(kù),就是在庫(kù)里找到 .o 然后丟到而可執(zhí)行程序里就行。
clean 部分我們把 *.a 加進(jìn)去就行了,這樣我們就可以 make clean 了:
現(xiàn)在,我們的 libmymath.a 就生成出來(lái)了,下面我們要做的是發(fā)布:
libmymath.a : mymath.o myprint.o
ar -rc libmymath.a mymath.o myprint.o
mymath.o : mymath.c
gcc -c mymath.c -o mymath.o
myprint.o : myprint.c
gcc -c myprint.c -o myprint.o
.PHONY : static
static:
mkdir -p lib-static/lib
mkdir -p lib-static/include
cp *.a lib-static/lib
cp *.h lib-static/include
.PHONY : clean
clean:
rm -f *.o *.a libmypath.a # 刪除
?? 結(jié)果如下:
0x03 生成動(dòng)態(tài)庫(kù)
動(dòng)態(tài)庫(kù)比靜態(tài)庫(kù)要復(fù)雜一些,在形成時(shí)原理跟靜態(tài)庫(kù)基本是一樣的。
gcc -shared
區(qū)別在于 形成 .o 的時(shí)候是需要加上 gcc -fPIC?的,這是為了產(chǎn)生 與位置無(wú)關(guān)碼。
libmymath.so : mymath.o myprint.o
gcc -shared -o libmymath.so mymath.o myprint.o
mymath.o : mymath.c
gcc -fPIC -c mymath.c -o mymath.o
myprint.o : myprint.c
gcc -fPIC -c myprint.c -o myprint.o
.PHONY:clean
clean:
rm -f *.o *.so
?? 結(jié)果如下:
此時(shí)我們 make 的時(shí)候就會(huì)先根據(jù) gcc -fPIC 形成與位置無(wú)關(guān)的 .o,
然后通過(guò) gcc -shared 的選項(xiàng)生成 .so 文件,此時(shí)就有了動(dòng)態(tài)庫(kù)。
動(dòng)態(tài)庫(kù)的交付:
libmymath.so : mymath.o myprint.o
gcc -shared -o libmymath.so mymath.o myprint.o
mymath.o : mymath.c
gcc -fPIC -c mymath.c -o mymath.o
myprint.o : myprint.c
gcc -fPIC -c myprint.c -o myprint.o
.PHONY:dyl
dyl:
mkdir -p lib-dyl/lib
mkdir -p lib-dyl/include
cp *.so lib-dyl/lib
cp *.h lib-dyl/include
.PHONY:clean
clean:
rm -f *.o *.so dyl
?? 結(jié)果如下:
?
0x04 同時(shí)生成動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)
那我們直接把兩個(gè) Makefile 合到一起看看:
.PHONY:all
all: libmymath.so libmymath.a
# 動(dòng)態(tài)庫(kù)
libmymath.so : mymath.o myprint.o
gcc -shared -o libmymath.so mymath.o myprint.o
mymath.o : mymath.c
gcc -fPIC -c mymath.c -o mymath.o
myprint.o : myprint.c
gcc -fPIC -c myprint.c -o myprint.o
# 靜態(tài)庫(kù)
libmymath.a : mymath.o myprint.o
ar -rc libmymath.a mymath.o myprint.o
mymath.o : mymath.c
gcc -c mymath.c -o mymath.o
myprint.o : myprint.c
gcc -c myprint.c -o myprint.o
# 發(fā)布
.PHONY : lib
lib:
mkdir -p lib-static/lib
mkdir -p lib-static/include
cp *.a lib-static/lib
cp *.h lib-static/include
mkdir -p lib-dyl/lib
mkdir -p lib-dyl/include
cp *.so lib-dyl/lib
cp *.h lib-dyl/include
# 清理
.PHONY : clean
clean:
rm -f *.o *.a lib
這樣是行的,都要形成 .o,到底是位置有關(guān)還是位置無(wú)關(guān)?最終帶來(lái)的結(jié)果就是不一樣。
所以名字要區(qū)分開(kāi)來(lái),你生成你的,我生成我的:
?.PHONY:all
all: libmymath.so libmymath.a
libmymath.so : mymath.o myprint.o
gcc -shared -o libmymath.so mymath.o myprint.o
mymath.o : mymath.c
gcc -fPIC -c mymath.c -o mymath.o
myprint.o : myprint.c
gcc -fPIC -c myprint.c -o myprint.o
libmymath.a : mymath_s.o myprint_s.o
ar -rc libmymath.a mymath_s.o myprint_s.o
mymath_s.o : mymath.c
gcc -c mymath.c -o mymath_s.o
myprint_s.o : myprint.c
gcc -c myprint.c -o myprint_s.o
.PHONY : lib
lib:
mkdir -p lib-static/lib
mkdir -p lib-static/include
cp *.a lib-static/lib
cp *.h lib-static/include
mkdir -p lib-dyl/lib
mkdir -p lib-dyl/include
cp *.so lib-dyl/lib
cp *.h lib-dyl/include
.PHONY : clean
clean:
rm -f *.o *.a *.so lib
?? 運(yùn)行結(jié)果:
?這樣,就既有動(dòng)態(tài)庫(kù)也有靜態(tài)庫(kù)了。
0x05 使用靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
現(xiàn)在我們站在使用的人的角度,學(xué)習(xí)如何使用靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)。
?? 代碼演示:
#include "mymath.h"
#include "myprint.h"
int main()
{
int start = 0;
int end = 0;
int result = addToVal(start, end);
printf("result: %d\n", result);
Print("Hello, World!");
return 0;
}
?? 代碼運(yùn)行:gcc mytest.c
此時(shí)必然是報(bào)錯(cuò)的,這樣頭文件是找不到的。我們來(lái)回顧一下頭文件的搜索路徑:
① 在當(dāng)前路徑下查找頭文件??
② 在系統(tǒng)路徑下查找頭文件?
我們自己寫的庫(kù)當(dāng)前的頭文件一定不在當(dāng)前的系統(tǒng)中,你當(dāng)前的頭文件不在當(dāng)前路徑下!
它既不在當(dāng)前路徑,也不在頭文件中,這自然是找不到頭文件的。
誰(shuí)在找頭文件?編譯器在找。系統(tǒng)中的頭文件一般在 lib64 路徑下,會(huì)存著大量的動(dòng)靜態(tài)庫(kù)。
第一種做法:將自己的頭文件和庫(kù)文件拷貝到系統(tǒng)路徑下即可。
gcc -l? ?指定我要鏈接的庫(kù)的名稱
我們還可以指定頭文件搜索路徑:
$ gcc mytest.c -o mytest -I ./lib-static/include/
此時(shí)鏈接還是失敗的。
?
$ gcc mytest.c -o mytest -I ./lib-static/include/ -L ./lib-static/lib/ -lmymath
此時(shí)就形成了 mytest。
-Ⅰ?表示我們的頭文件查找的路徑
-L 表示庫(kù)文件搜索的路徑
-l 在-L 指定的路徑下你要鏈接哪一個(gè)庫(kù)
動(dòng)態(tài)庫(kù):
gcc mytest.c -o mytest -I lib-dyl/include/ -L lib-dyl/lib/ -lmymath
形成可執(zhí)行程序之后,已經(jīng)把需要的代碼拷貝到我的代碼中,運(yùn)行時(shí)不依賴你的庫(kù)。不需要運(yùn)行時(shí)查找。
為什么動(dòng)態(tài)庫(kù)會(huì)有這個(gè)問(wèn)題?想辦法讓進(jìn)程找到動(dòng)態(tài)庫(kù)即可。
error while loading shared libraries 解決方案:
① 動(dòng)態(tài)庫(kù)拷貝到系統(tǒng)路徑下 /lib64 安裝。
② 通過(guò)導(dǎo)入環(huán)境變量的方式 —— 程序運(yùn)行的時(shí)候,會(huì)在環(huán)境變量中查找自己需要的動(dòng)態(tài)庫(kù)路徑 —— LD_LIBRARY_PATH。
③ 系統(tǒng)配置文件來(lái)做。
?? [ 筆者 ]? ?王亦優(yōu)
?? [ 更新 ]? ?2023.7.20
? [ 勘誤 ]?? /* 暫無(wú) */
?? [ 聲明 ]? ?由于作者水平有限,本文有錯(cuò)誤和不準(zhǔn)確之處在所難免,
本人也很想知道這些錯(cuò)誤,懇望讀者批評(píng)指正!
?? 參考資料? C++reference[EB/OL]. []. http://www.cplusplus.com/reference/. Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. . 百度百科[EB/OL]. []. https://baike.baidu.com/.文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-622440.html 比特科技. Linux[EB/OL]. 2021[2021.8.31 xi文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-622440.html |
到了這里,關(guān)于【看表情包學(xué)Linux】軟硬鏈接 | 軟連接數(shù) | 創(chuàng)建軟硬鏈接 | 動(dòng)靜態(tài)庫(kù) | 生成靜態(tài)庫(kù) | 生成動(dòng)態(tài)庫(kù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!