Linux:基礎IO(檔案描述符分配規則)(重定向)(inode)(軟硬連結)(動態庫靜態庫)
目錄
檔案描述符的分配規則
最小分配原則
通過程式碼理解:
//這是一個演示檔案描述符分配的demo //1:檔案描述符是一個數字,並且這個數字是一個結構體的下標 //分規則:尋找最小的未使用下標 #include<stdio.h> #include<unistd.h> #include<fcntl.h> #include<errno.h> //#include<sys/types.h> //#include<sys/stat.h> int main() { int fd = -1; fd = open("./test",O_RDONLY | O_CREAT); if(fd < 0) { perror("open"); return -1; } printf("fd:%d\n",fd); close(fd); return 0; }
輸出為:fd:3
關閉0/2
#include<stdio.h> #include<unistd.h> #include<fcntl.h> #include<errno.h> int main() { int fd = -1; fd = open("./test",O_RDONLY | O_CREAT); if(fd < 0) { perror("open"); return -1; } printf("fd:%d\n",fd); close(fd); return 0; }
結果為 fd:0
檔案描述符分配規則:在file_struct陣列中,找到當前沒有被使用的最小的一個下標,作為新的檔案描述符
重定向原理
將原本描述符所對應的下標檔案描述符修改成另一個檔案的描述符,這樣的話描述符沒有變,
但是真正通過描述符操作的這個檔案已經改變了
如果關閉1:
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<errno.h> int main() { close(1); int fd = open("tmp",O_WRONLY | O_CREAT,0644); if(fd < 0) { perror("open"); return -1; } printf("fd:%d\n",fd); fflush(stdout); return 0; }
本來應該輸出到顯示器上的內容,輸出到了檔案tmp上。其中fd = 1
這種現象的本質叫做輸出的重定向。常見的重定向有>,>>,<
函式名: dup2
功能: 複製檔案描述符
用法: int dup2(int oldfd,int newfd);
printf()是c庫的IO函式,一般往stdout輸出,但是stdout在底層訪問檔案的時候還是找fd:1,
但是fd:1的下標所標示的內容已經變成了myfile的地址,不再是顯示器檔案的地址,所以
任何訊息都會往檔案中寫入,進而完成輸出重定向
總結:本質上就是改變描述符(下標)所對應的檔案描述符
FILE
因為IO相關的函式系統呼叫的介面對應,並且庫函式封裝系統呼叫
所以本質上訪問檔案都是通過fd訪問的,所以c庫中的FILE結構體內部,必定封裝了fd
#include<stdio.h>
#include<string.h>
int main()
{
const char *msg1 = "printf\n";
const char *msg2 = "fwrite\n";
const char *msg3 = "write\n";
printf("%s",msg1);
fwrite(msg2,strlen(msg2),1,stdout);
write(1,msg3,strlen(msg3));
fork();
return 0;
}
執行結果:
為什麼對程序實現輸出重定向結果會改變呢?
printf和fwrite都輸出兩遍(庫函式),而write只輸出了一次(系統呼叫)
肯定與fork有關
- 一般c庫檔案寫入函式時是全緩衝的,而寫入顯示器是行緩衝的
- printf和fwrite庫函式會自帶緩衝區,當發生重定向到普通檔案時,資料的緩衝方式由行緩衝變成了全緩衝
- 我們放在緩衝區的資料就不會被立刻重新整理
- 但是在程序退出的時候會統一重新整理,寫入檔案中
- fork時父子資料會發生寫時拷貝,所以當父程序準備重新整理的時候,子程序也就有了同樣的一份資料,隨即產生兩份資料
- write沒有變化,所以沒有所謂的緩衝
總結
printf和fwrite庫函式都會自帶緩衝區,而write系統呼叫沒有緩衝區
我們這裡所說的都是使用者級緩衝區,庫函式有,而系統呼叫沒有,庫函式是系統呼叫的上層
是對系統呼叫的封裝,足以說明該緩衝區是二次加上的,因為又是c,所以由c標準庫提供
理解檔案系統
ls -l:(stat命令也可以)
除了看見檔名還有相關資料:
- 模式
- 硬連結數
- 檔案所有者
- 組
- 大小
- 最後修改時間
- 檔名
A:最後訪問時間
M:檔案內容最後修改時間
C:屬性最後修改時間
inode是什麼
- 檔案儲存在硬碟上,硬碟的最小儲存單位叫做"扇區"(Sector)。每個扇區儲存512位元組(相當於0.5KB)。
- 作業系統讀取硬碟的時候,不會一個個扇區地讀取,這樣效率太低,而是一次性連續讀取多個扇區,即一次性讀取一個"塊"(block)。這種由多個扇區組成的"塊",是檔案存取的最小單位。"塊"的大小,最常見的是4KB,即連續八個 sector組成一個 block。
- 檔案資料都儲存在"塊"中,那麼很顯然,我們還必須找到一個地方儲存檔案的元資訊,比如檔案的建立者、檔案的建立日期、檔案的大小等等。這種儲存檔案元資訊的區域就叫做inode,中文譯名為"索引節點"。
inode內容
inode包含檔案的元資訊,具體來說有以下內容:
-
檔案的位元組數
-
檔案擁有者的User ID
-
檔案的Group ID
-
檔案的讀、寫、執行許可權
-
檔案的時間戳,共有三個:ctime指inode建立時間,mtime指檔案內容上一次修改的時間,atime指檔案最後一次訪問的時間。
-
連結數,即有多少檔名指向這個inode
-
檔案資料block的位置
目錄項:目錄下的檔名和檔案inode節點號
查詢一個檔案流程:開啟目錄檔案,讀取其中目錄項資訊,通過檔名來找到檔案的inode節點號
在通過inode節點號找到檔案系統在inode區域中的inode節點,再通過inode節點中儲存的檔案資料儲存位置
找到檔案在資料區域儲存的資料
建立一個新檔案操作
- 儲存屬性:核心先找一個空閒的節點,把檔案資訊記錄到其中
- 資料儲存:可以將資訊分塊記錄在不同磁碟塊中
- 記錄分配情況:核心在inode的磁碟分佈區間記錄存放塊位置順序
- 新增檔名到目錄:核心將入口新增到目錄,檔名和inode之間對應關係將檔名和檔案的內容及書寫連線起來
硬連結
我們在磁碟上找到的檔案並不是文加名,而是indoe,其實在linux中可以讓多個檔名對應一個inode
硬連結更像是一個檔案的別名,他有自己的目錄項,但是並沒有單獨的inode節點和資料區,它的inode節點和資料區是和原始檔是相同的,一個檔案,每多一個硬連結,那麼它的inode節點中有一個連結數屬性,都會+1,代表這個inode節點對應了好多個檔名,可以通過任何一個檔案目錄項訪問到相同的inode節點。
那麼我們刪除一個檔案時候,實際上是先將inode節點中的連結數-1操作,黨政連結數為0時,就認為這個檔案真的要被刪除了,這時候才會釋放inode節點和資料區
- Q和P連結狀態完全相同,它們被稱為指向檔案的硬連結,核心記錄了這個連線數,inode787455的硬連結數為2
- 我們刪除檔案時幹了兩件事,1:在目錄中將對應的記錄刪除2:將硬連結數-1,如果為0對應的磁碟釋放
軟連結
硬連結是通過inode引用另外一個檔案,軟連結是通過名字引用另外一個檔案
軟連結是一個獨立的檔案,有自己的目錄項,inode節點,資料區
可以看做是一個快捷方式,通過這個軟連結可以找到另一個為位置的其他檔案
軟連結檔案是linux的特殊型別檔案(符號連結檔案 |)這個檔案中所儲存的數 據就是指向另一個檔案的路徑名,這個軟連結檔案就是通過這個路徑名來定位所指向的檔案
檔案具備所有許可權,並且檔案型別是l(link:連結(符號連結檔案:軟連結))
軟硬連結區別:
- 軟連結可以針對目錄建立,硬連結不可以
- 軟連結是一個新的檔案,而硬連結是原始檔的一個別名(與原始檔使用相同的inode)
- 軟連結可以跨分割槽建立(記錄路徑可以找見),硬連結不可以(不同分割槽inode節點指向不同地方)
- 刪除原始檔後,軟連結找不到原始檔,硬連結無影響(inode連結數-1)
動態庫和靜態庫
- gcc預設連結方式:動態連結 ---動態庫
- 動態庫:程式執行時才去連結動態庫的程式碼,多個程式可以共享使用庫的程式碼
- 靜態連結需要加上 -static ---靜態庫
- 靜態庫:編譯時把程式碼連結到可執行檔案中。程式執行的時候將不再需要靜態庫
- 原則上都是將所寫的檔案打包成一個檔案供別人使用
如何生成自己的動態庫和靜態庫
- 程式編譯過程:預處理---編譯---彙編---連結
- 生成一個庫其實就是將所有的程式碼打包起來,最終得到一個庫檔案
- 生成動態庫:gcc
- 生成靜態庫:ar
- 需要先將原始碼檔案編譯成自己的目標檔案(動態庫可以gcc直接從原始碼生成),也就是從c ---.o
- 最終這些.o檔案連結在一起生成動態庫還是靜態庫或者可執行檔案,取決於連結過程!
- 如果要生成的是一個庫檔案,那麼這些程式碼中不能有main函式
如果要生成的是一個可執行程式,那麼這些程式碼中有切只能有一個main函式 - 靜態庫:將所有的.o檔案打包到一起生成
- 生成命令:ar -cr lib檔名.a 檔名.a(-c 建立 -r 模組替換)
- 動態庫生成:--share是生成動態庫的gcc連結選項,沒有這個將認為生成可執行程式將所有.o檔案打包
- 生成命令: gcc --share 檔名.o -o lib檔名.so
- 要生成一個動態庫,gcc編譯器告訴我們,在編譯階段,將一個.c--- .o需要加上一個編譯選項
-fPIC:產生位置無關程式碼
使用 -fPIC 選項,會生成 PIC 程式碼。.so 要求為 PIC,以達到動態連結的目的,否則,無法實現動態連結
(動態庫對映到虛擬地址空間時對應的位置是不確定的,因此不能生成絕對路徑的地址) - 動態庫是一個執行時庫,需要在程式執行時也載入到記憶體中,並這個載入的過程是作業系統乾的,回去指定位置載入動態庫,因為我們需要將動態庫放到指定的位置才可以執行程式
如何連結一個庫生成可執行程式
gcc main.c -o main -L./ -ltest
-Lpath 用於指定庫的查詢路徑
-lname 用於指定連結的庫名稱(去掉前後綴)