Linux系統編程【4】——文件系統

pwd命令的作用

Linux的文件系統比較龐大,所以筆者從pwd這一命令入手,在實現的過程中加深對文件系統的了解。

輸入:man pwd

在這裡插入圖片描述

從指導文檔中可以看到,pwd命令的作用是顯示出當前所處位置,以路徑的形式打印出來。

舉例如下:

在這裡插入圖片描述

筆者首先輸入pwd命令,顯示出/home/lularible,說明我當前就處在該位置。

當筆者進入其中的一個子目錄”bin”時,再次輸入pwd命令,顯示出/home/lularible/bin,這是顯而易見的。

Linux文件系統內部結構

為了能夠實現pwd命令,就需要先了解Linux文件系統的內部結構

文件系統的多層抽象

一般而言,文件是存儲在硬盤上的,那麼將磁盤這一物理實體,進行邏輯劃分和組織,就是進行抽象的過程。目的就是為了便於管理。

最樸素的管理手段就是,給硬盤的區域編號,按照編號從低到高給文件分配存儲空間。當然,訪問效率就可想而知了(當文件數量一多,訪問效率將會及其低下)。所以那些系統開發人員就發揮出自己的聰明才智,對文件系統進行了巧妙的設計。

第一層抽象:從磁盤到分區

一整塊磁盤,能存儲大量的數據,統一管理起來不太方便。若能將它分而治之,則更加靈活。比如說window系統裏面的磁盤分區:C盤,D盤,E盤等等。每個分區都可以看作是一個獨立的磁盤。

第二層抽象:從磁盤到塊序列

一個磁盤由一些磁性盤片組成。每個盤片的表面都被劃分為很多同心圓,這些同心圓稱作磁道,每一個磁道又進一步被劃分為扇區,每個扇區可以存儲一定位元組的數據(如512位元組)。扇區是磁盤上的基本存儲單元,每次空間分配或者訪問都是以一個扇區為最小單位。

第三層抽象:從塊序列到三個區域的劃分

為了存儲不同類型的數據(文件內容、文件屬性、目錄),將磁盤塊分為3個部分:

在這裡插入圖片描述

  • 1.超級塊:文件系統中的第一個塊被稱為超級塊,用來存放文件系統本身的結構信息。
  • 2.i-節點表:文件系統的下一個部分被稱為i-節點表。每個文件都有一些屬性,如大小、文件所有者和最近修改時間等。這些性質被記錄在一個稱為i-節點的結構中。所有的i-節點都有相同的大小,並且i-節點表是這些結構的一個列表。
  • 3.數據區:文件系統的第三部分是數據區。文件的內容保存在這個區域。

創建一個文件的過程

文件有內容和屬性,內核將文件內容存放在數據區,文件屬性存放在i-節點,文件名存放在目錄。下圖所示為創建一個文件的例子:

在這裡插入圖片描述

其中的幾個主要操作為:

  • 1.存儲屬性:內核找到一個空的i-節點(圖中為47),把文件信息記錄到該i-節點中。
  • 2.存儲數據:內核從空閑磁盤塊表中獲得627、200、992這三個磁盤塊,將內核緩衝區中的數據複製到這三個磁盤塊中。
  • 3.記錄分配情況:文件內容按序存放在磁盤塊627/200/992中。內核在i-節點的磁盤分佈區記錄了上述序列塊。磁盤分佈區是一個磁盤塊序號的列表,這三個編號放在最開始的三個位置。
  • 4.添加文件名到目錄:新文件的名字是userlist。內核將入口(47,userlist)添加到目錄文件。文件名和i-節點號之間的對應關係將文件名和文件的內容及屬性連接了起來。

目錄的結構

目錄被抽象為一個包含i-節點號和文件名的表。

輸入ls -ia可以查看當前目錄包含的文件以及對應的i-節點號:

在這裡插入圖片描述

如代表當前目錄的”.”的i-節點號為1048600,代表上一級目錄的”..”的i-節點號為936372。再看一下根目錄的組成:

在這裡插入圖片描述

其中”.”和”..”的i-節點號都是2,說明根目錄的上級目錄就是自己。另外,還可以看到存在i-節點號一樣的文件(目錄),這就是硬鏈接,將在後續的軟、硬鏈接區別一節中敘述。

文件讀取的工作原理

既然知道了目錄的結構,那麼就可以理一遍文件讀取的過程了。舉例來說:

輸入cat userlist,表示讀取userlist這個文件並顯示。

在這裡插入圖片描述

step1.在目錄中尋找文件名

文件名存儲在目錄文件中,內核在目錄文件中尋找包含字符串”userlist”的記錄。userlist所在的記錄包含編號為47的i-節點。

step2.定位i-節點並讀取內容

內核在文件系統中的i-節點區域找到i-節點47。i-節點包含數據塊編號的列表。

step3.訪問存儲文件內容的數據塊

在step2之後,即知道了數據塊的位置和在磁盤中的順序,就可以通過不停的調用read函數,使內核不斷將位元組從磁盤複製到內核緩衝區。

ps:讀取權限的控制:內核根據文件名找到i-節點號,再根據i-節點號找到i-節點。在i-節點中,找到文件的權限位和用戶ID,從而判斷該用戶是否具有讀取權限。

大文件的i-節點

每個文件對應一個i-節點,而每個i-節點大小是固定的,所以當文件內容需要佔用很多磁盤塊時,i-節點中的磁盤分佈區可能不夠用。這個時候,就需要採用多級分配的方式。一圖說明問題:

在這裡插入圖片描述

其中,級數越多,訪問磁盤時就越慢。因為需要按照每一級的指引,多次讀取磁盤。

實現pwd

思路

  • 1.獲得當前目錄的i-節點號,記為stat
  • 2.改變目錄至上級目錄
  • 3.找到i-節點號stat的名字

重複上述步驟,直到樹頂。到樹頂的判斷依據:一個目錄的”.”和”..”的i-節點號相同。

源代碼

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<dirent.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

ino_t get_inode(char*);
void printpathto(ino_t);
void inum_to_name(ino_t,char*,int);

int main()
{
	printpathto(get_inode("."));				//打印當前位置(文件路徑) 
	putchar('\n');
	return 0;
}
 
void printpathto(ino_t this_inode)
{
	ino_t my_inode;
	char its_name[BUFSIZ];
	if(get_inode("..") != this_inode)
	{
		chdir("..");					//往上一級	
		inum_to_name(this_inode,its_name,BUFSIZ);	//獲得i-節點號對應的文件名 
		my_inode = get_inode(".");			//獲得當前目錄節點號 
		printpathto(my_inode);				//遞歸打印 
		printf("/%s",its_name);				//打印文件名 
	}
}

//獲得i-節點號對應的文件名 
void inum_to_name(ino_t inode_to_find,char* namebuf,int buflen)
{
	DIR* dir_ptr;
	struct dirent* direntp;
	dir_ptr = opendir(".");
	if(dir_ptr == NULL){
		perror(".");
		exit(1);
	}
	while((direntp = readdir(dir_ptr)) != NULL){
		if(direntp->d_ino == inode_to_find){
			strncpy(namebuf,direntp->d_name,buflen);
			namebuf[buflen-1] = '\0';
			closedir(dir_ptr);
			return;
		}
	}
	fprintf(stderr,"error looking for inum %ld\n",inode_to_find);
	exit(1);
}

//獲得文件名對應的i-節點號 
ino_t get_inode(char* fname)
{
	struct stat info;
	if(stat(fname,&info) == -1){
		fprintf(stderr,"Cannot stat");
		perror(fname);
		exit(1);
	}
	return info.st_ino;
}

當pwd01命令運行在裝載文件系統中時,可能會出現與原版pwd不同的結果。(裝載文件系統是指將它嵌入到已有的系統以獲得某些支持,子樹的根目錄被嵌入到根文件系統的一個目錄中,子樹所在的目錄被稱為第二個系統的裝載點。)依據pwd01命令的實現邏輯,當遇到當前目錄和上級目錄i-節點號一樣時,就停止上溯,實際上可能只是到達了一個被裝載的文件系統的頂端,而非整個文件系統的頂端。

硬鏈接和軟鏈接(符號鏈接)

假設多個文件名通過硬鏈接的方式指向同一個文件,那麼它們擁有同一個i-節點,系統會記錄鏈接數。如果刪除其中的一個文件,則會將鏈接數減一,直到鏈接數為0時,才會真正刪除文件。

而建立原文件的軟鏈接,則其i-節點號與原文件的不同,並且鏈接數、修改時間和文件大小都不同於原始文件。若原始文件被刪除或者位置變動或者被改名,那麼軟鏈接將指向空。這種思想有點類似於window中的快捷方式。

參考資料

《Understanding Unix/Linux Programming A Guide to Theory and Practice》

歡迎大家轉載本人的博客(需註明出處),本人另外還有一個個人博客網站:lularible的個人博客,歡迎前去瀏覽。