1. 程式人生 > >Linux:基礎IO(檔案描述符分配規則)(重定向)(inode)(軟硬連結)(動態庫靜態庫)

Linux:基礎IO(檔案描述符分配規則)(重定向)(inode)(軟硬連結)(動態庫靜態庫)

目錄

檔案描述符的分配規則

重定向原理

FILE

總結

理解檔案系統

inode是什麼

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有關

  1. 一般c庫檔案寫入函式時是全緩衝的,而寫入顯示器是行緩衝的
  2. printf和fwrite庫函式會自帶緩衝區,當發生重定向到普通檔案時,資料的緩衝方式由行緩衝變成了全緩衝
  3. 我們放在緩衝區的資料就不會被立刻重新整理
  4. 但是在程序退出的時候會統一重新整理,寫入檔案中
  5. fork時父子資料會發生寫時拷貝,所以當父程序準備重新整理的時候,子程序也就有了同樣的一份資料,隨即產生兩份資料
  6. write沒有變化,所以沒有所謂的緩衝

總結

printf和fwrite庫函式都會自帶緩衝區,而write系統呼叫沒有緩衝區

我們這裡所說的都是使用者級緩衝區,庫函式有,而系統呼叫沒有,庫函式是系統呼叫的上層

是對系統呼叫的封裝,足以說明該緩衝區是二次加上的,因為又是c,所以由c標準庫提供

理解檔案系統

ls -l:(stat命令也可以)

除了看見檔名還有相關資料:

  1. 模式
  2. 硬連結數
  3. 檔案所有者
  4. 大小
  5. 最後修改時間
  6. 檔名

A:最後訪問時間

M:檔案內容最後修改時間

C:屬性最後修改時間

inode是什麼

  1. 檔案儲存在硬碟上,硬碟的最小儲存單位叫做"扇區"(Sector)。每個扇區儲存512位元組(相當於0.5KB)。
  2. 作業系統讀取硬碟的時候,不會一個個扇區地讀取,這樣效率太低,而是一次性連續讀取多個扇區,即一次性讀取一個"塊"(block)。這種由多個扇區組成的"塊",是檔案存取的最小單位。"塊"的大小,最常見的是4KB,即連續八個 sector組成一個 block。
  3. 檔案資料都儲存在"塊"中,那麼很顯然,我們還必須找到一個地方儲存檔案的元資訊,比如檔案的建立者、檔案的建立日期、檔案的大小等等。這種儲存檔案元資訊的區域就叫做inode,中文譯名為"索引節點"。

inode內容

inode包含檔案的元資訊,具體來說有以下內容:

  1.  檔案的位元組數

  2. 檔案擁有者的User ID

  3.  檔案的Group ID

  4.  檔案的讀、寫、執行許可權

  5. 檔案的時間戳,共有三個:ctime指inode建立時間,mtime指檔案內容上一次修改的時間,atime指檔案最後一次訪問的時間。

  6.  連結數,即有多少檔名指向這個inode

  7.  檔案資料block的位置

目錄項:目錄下的檔名和檔案inode節點號

查詢一個檔案流程:開啟目錄檔案,讀取其中目錄項資訊,通過檔名來找到檔案的inode節點號

在通過inode節點號找到檔案系統在inode區域中的inode節點,再通過inode節點中儲存的檔案資料儲存位置

找到檔案在資料區域儲存的資料

建立一個新檔案操作

  1. 儲存屬性:核心先找一個空閒的節點,把檔案資訊記錄到其中
  2. 資料儲存:可以將資訊分塊記錄在不同磁碟塊中
  3. 記錄分配情況:核心在inode的磁碟分佈區間記錄存放塊位置順序
  4. 新增檔名到目錄:核心將入口新增到目錄,檔名和inode之間對應關係將檔名和檔案的內容及書寫連線起來

硬連結

我們在磁碟上找到的檔案並不是文加名,而是indoe,其實在linux中可以讓多個檔名對應一個inode

硬連結更像是一個檔案的別名,他有自己的目錄項但是並沒有單獨的inode節點和資料區,它的inode節點和資料區是和原始檔是相同的,一個檔案,每多一個硬連結,那麼它的inode節點中有一個連結數屬性,都會+1,代表這個inode節點對應了好多個檔名,可以通過任何一個檔案目錄項訪問到相同的inode節點。

那麼我們刪除一個檔案時候,實際上是先將inode節點中的連結數-1操作,黨政連結數為0時,就認為這個檔案真的要被刪除了,這時候才會釋放inode節點和資料區

 

  1. Q和P連結狀態完全相同,它們被稱為指向檔案的硬連結,核心記錄了這個連線數,inode787455的硬連結數為2
  2. 我們刪除檔案時幹了兩件事,1:在目錄中將對應的記錄刪除2:將硬連結數-1,如果為0對應的磁碟釋放

軟連結

硬連結是通過inode引用另外一個檔案,軟連結是通過名字引用另外一個檔案

軟連結是一個獨立的檔案,有自己的目錄項,inode節點,資料區

可以看做是一個快捷方式,通過這個軟連結可以找到另一個為位置的其他檔案

軟連結檔案是linux的特殊型別檔案(符號連結檔案 |)這個檔案中所儲存的數 據就是指向另一個檔案的路徑名,這個軟連結檔案就是通過這個路徑名來定位所指向的檔案

檔案具備所有許可權,並且檔案型別是l(link:連結(符號連結檔案:軟連結))

軟硬連結區別:

  1. 軟連結可以針對目錄建立,硬連結不可以
  2. 軟連結是一個新的檔案,而硬連結是原始檔的一個別名(與原始檔使用相同的inode)
  3. 軟連結可以跨分割槽建立(記錄路徑可以找見),硬連結不可以(不同分割槽inode節點指向不同地方)
  4. 刪除原始檔後,軟連結找不到原始檔,硬連結無影響(inode連結數-1)

動態庫和靜態庫

  1. gcc預設連結方式:動態連結 ---動態庫
  2. 動態庫:程式執行時才去連結動態庫的程式碼,多個程式可以共享使用庫的程式碼
  3. 靜態連結需要加上 -static   ---靜態庫
  4. 靜態庫:編譯時把程式碼連結到可執行檔案中。程式執行的時候將不再需要靜態庫
  5. 原則上都是將所寫的檔案打包成一個檔案供別人使用

如何生成自己的動態庫和靜態庫

  1. 程式編譯過程:預處理---編譯---彙編---連結
  2. 生成一個庫其實就是將所有的程式碼打包起來,最終得到一個庫檔案
  3. 生成動態庫:gcc
  4. 生成靜態庫:ar 
  5. 需要先將原始碼檔案編譯成自己的目標檔案(動態庫可以gcc直接從原始碼生成),也就是從c  ---.o
  6. 最終這些.o檔案連結在一起生成動態庫還是靜態庫或者可執行檔案,取決於連結過程!
  7. 如果要生成的是一個庫檔案,那麼這些程式碼中不能有main函式
    如果要生成的是一個可執行程式,那麼這些程式碼中有切只能有一個main函式
  8. 靜態庫:將所有的.o檔案打包到一起生成
  9. 生成命令:ar  -cr  lib檔名.a  檔名.a(-c 建立 -r  模組替換)
  10. 動態庫生成:--share是生成動態庫的gcc連結選項,沒有這個將認為生成可執行程式將所有.o檔案打包
  11. 生成命令: gcc   --share  檔名.o  -o  lib檔名.so
  12. 要生成一個動態庫,gcc編譯器告訴我們,在編譯階段,將一個.c--- .o需要加上一個編譯選項
    -fPIC:產生位置無關程式碼
    使用 -fPIC 選項,會生成 PIC 程式碼。.so 要求為 PIC,以達到動態連結的目的,否則,無法實現動態連結
    (動態庫對映到虛擬地址空間時對應的位置是不確定的,因此不能生成絕對路徑的地址)
  13. 動態庫是一個執行時庫,需要在程式執行時也載入到記憶體中,並這個載入的過程是作業系統乾的,回去指定位置載入動態庫,因為我們需要將動態庫放到指定的位置才可以執行程式

如何連結一個庫生成可執行程式

gcc main.c -o main  -L./  -ltest

-Lpath 用於指定庫的查詢路徑

-lname 用於指定連結的庫名稱(去掉前後綴)