Linux檔案系統中的inode索引結點 VFS inode
Linux中VFS檔案系統的組織
當我們讀取一個檔案時,實際上是在目錄中找到了這個檔案的inode編號,然後根據inode的指標,把硬碟中資料塊組合起來,放入記憶體供進一步的處理。
當我們寫入一個檔案時,是分配一個空閒inode給該檔案,將其inode編號記入該檔案所屬的目錄,然後選取空閒的資料塊,讓inode的指標指向這些資料塊,並放入記憶體中的資料。
硬碟扇區 sector
檔案儲存在硬碟上,硬碟的最小儲存單位叫"扇區"(Sector),每個扇區512位元組。
塊 block
作業系統讀取硬碟的時候,不會一個個扇區地讀取,這樣效率太低,而是一次性連續讀取多個扇區,即一次性讀取一個"塊"(block)。這種由多個扇區組成的"塊"
"塊"的大小,最常見的是4KB,即連續八個sector組成一個 block。
索引結點 inode
有一個結構儲存檔案的元資訊,叫做"索引節點"(inode)。
每一個檔案(包括目錄)都有對應的inode,裡面包含了與該檔案有關的資訊(除了檔名)。可以用“stat + 檔名”命令來檢視。
Size 檔案大小 Blocks 塊編號 IO Blocks block的大小 檔案型別: 普通檔案/目錄/符號連結 Device 裝置編號 Inode 索引結點編號 Links 硬連結數 Access 訪問許可權 Uid 使用者id Gid 組id Acess Modify Change 三個時間
檢視每個硬碟分割槽的inode總數和已經使用的數量,可以使用df -i命令。
檢視每個inode的大小,sudo dumpe2fs -h /dev/hda | grep "Inode size"
作業系統會把硬碟分成兩個區域:一個是資料區,存放檔案資料;另一個是inode區(inode table),存放inode。inode節點的總數,在格式化時就給定,一般是每1KB或每2KB就設定一個inode。由於每個檔案都必須有一個inode,因此有可能發生inode已經用光,但是硬碟還未存滿的情況。這時就無法在硬碟上建立新檔案。
硬連結 hard link
多個檔名指向同一個inode編號
對檔案內容進行修改,會影響到所有檔名;刪除一個檔名,不影響另一個檔名的訪問。
使用ln命令建立硬連結:“ln 原始檔 新檔案”
硬連結數
每建立一個硬連結,對應檔案的硬連結數就會增加1。刪除一個檔名,就會使得inode節點中的"連結數"減1。當這個值減到0,表明沒有檔名指向這個inode,系統就會回收這個inode號碼,以及其所對應block區域。
注意:目錄檔案的"連結數"。建立目錄時,預設會生成兩個目錄項:"."和".."。前者的inode號碼就是當前目錄的inode號碼,等同於當前目錄的"硬連結";後者的inode號碼就是當前目錄的父目錄的inode號碼,等同於父目錄的"硬連結"。所以,任何一個目錄的"硬連結"總數,總是等於2加上它的子目錄總數(含隱藏目錄)。
軟連結 symbolic link
檔案A和檔案B的inode編號不一樣,檔案A的內容是檔案B的路徑。
建立軟連線:“ln -s 源文檔案或目錄 目標檔案或目錄”
裝置號 device number
主裝置號:標識裝置驅動程式。 The major number identifies the driver associated with the device
副裝置號:用於具體確定裝置。The minor number is used by the kernel to determine exactly which device is being referred to.
inode結構體定義在Linux核心原始碼<linux/fs.h>中。
struct inode {
struct hlist_node i_hash; /* 雜湊表 */
struct list_head i_list; /* 索引節點連結串列 */
struct list_head i_dentry; /* 目錄項鍊表 */
unsigned long i_ino; /* 節點號 */
atomic_t i_count; /* 引用記數 */
umode_t i_mode; /* 訪問許可權控制 */
unsigned int i_nlink; /* 硬連結數 */
uid_t i_uid; /* 使用者id */
gid_t i_gid; /* 使用者id組 */
kdev_t i_rdev; /* 實裝置識別符號 */
loff_t i_size; /* 以位元組為單位的檔案大小 */
struct timespec i_atime; /* 最後訪問時間 */
struct timespec i_mtime; /* 最後修改(modify)時間 */
struct timespec i_ctime; /* 最後改變(change)時間 */
unsigned int i_blkbits; /* 以位為單位的塊大小 */
unsigned long i_blksize; /* 以位元組為單位的塊大小 */
unsigned long i_version; /* 版本號 */
unsigned long i_blocks; /* 檔案的塊數 */
unsigned short i_bytes; /* 使用的位元組數 */
spinlock_t i_lock; /* 自旋鎖 */
struct rw_semaphore i_alloc_sem; /* 索引節點訊號量 */
struct inode_operations *i_op; /* 索引節點操作表 */
struct file_operations *i_fop; /* 預設的索引節點操作 */
struct super_block *i_sb; /* 相關的超級塊 */
struct file_lock *i_flock; /* 檔案鎖鏈表 */
struct address_space *i_mapping; /* 相關的地址對映 */
struct address_space i_data; /* 裝置地址對映 */
struct dquot *i_dquot[MAXQUOTAS]; /* 節點的磁碟限額 */
struct list_head i_devices; /* 塊裝置連結串列 */
struct pipe_inode_info *i_pipe; /* 管道資訊 */
struct block_device *i_bdev; /* 塊裝置驅動 */
unsigned long i_dnotify_mask; /* 目錄通知掩碼 */
struct dnotify_struct *i_dnotify; /* 目錄通知 */
unsigned long i_state; /* 狀態標誌 */
unsigned long dirtied_when; /* 首次修改時間 */
unsigned int i_flags; /* 檔案系統標誌 */
unsigned char i_sock; /* 可能是個套接字吧 */
atomic_t i_writecount; /* 寫者記數 */
void *i_security; /* 安全模組 */
__u32 i_generation; /* 索引節點版本號 */
union {
void *generic_ip; /* 檔案特殊資訊 */
} u;
};
/*
*索引節點的操作inode_operations定義在linux/fs.h中
*/
struct inode_operations {
int (*create) (struct inode *, struct dentry *,int);
/*VFS通過系統呼叫create()和open()來呼叫該函式,從而為dentry物件建立一個新的索引節點。在建立時使用mode制定初始模式*/
struct dentry * (*lookup) (struct inode *, struct dentry *);
/*該韓式在特定目錄中尋找索引節點,該索引節點要對應於dentry中給出的檔名*/
int (*link) (struct dentry *, struct inode *, struct dentry *);
/*該函式被系統呼叫link()電泳,用來建立硬連線。硬連結名稱由dentry引數指定,連線物件是dir目錄中ld_dentry目錄想所代表的檔案*/
int (*unlink) (struct inode *, struct dentry *);
/*該函式被系統呼叫unlink()呼叫,從目錄dir中刪除由目錄項dentry制動的索引節點物件*/
int (*symlink) (struct inode *, struct dentry *, const char *);
/*該函式被系統電泳symlik()呼叫,建立符號連線,該符號連線名稱由symname指定,連線物件是dir目錄中的dentry目錄項*/
int (*mkdir) (struct inode *, struct dentry *, int);
/*該函式被mkdir()呼叫,建立一個新魯姆。建立時使用mode制定的初始模式*/
int (*rmdir) (struct inode *, struct dentry *);
/*該函式被系統呼叫rmdir()呼叫,刪除dir目錄中的dentry目錄項代表的檔案*/
int (*mknod) (struct inode *, struct dentry *, int, dev_t);
/*該函式被系統呼叫mknod()呼叫,建立特殊檔案(裝置檔案、命名管道或套接字)。要建立的檔案放在dir目錄中,其目錄項問dentry,關聯的裝置為rdev,初始許可權由mode指定*/
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
/*VFS呼叫該函式來移動檔案。檔案源路徑在old_dir目錄中,原始檔由old_dentry目錄項所指定,目標路徑在new_dir目錄中,目標檔案由new_dentry指定*/
int (*readlink) (struct dentry *, char *, int);
/*該函式被系統呼叫readlink()呼叫,拷貝資料到特定的緩衝buffer中。拷貝的資料來自dentry指定的符號連結,最大拷貝大小可達到buflen位元組*/
int (*follow_link) (struct dentry *, struct nameidata *);
/*該函式由VFS呼叫,從一個符號連線查詢他指向的索引節點,由dentry指向的連線被解析*/
int (*put_link) (struct dentry *, struct nameidata *);
/*在follow_link()呼叫之後,該函式由vfs呼叫進行清楚工作*/
void (*truncate) (struct inode *);
/*該函式由VFS呼叫,修改檔案的大小,在呼叫之前,索引節點的i_size項必須被設定成預期的大小*/
int (*permission) (struct inode *, int);
/*該函式用來檢查給低昂的inode所代表的檔案是否允許特定的訪問模式,如果允許特定的訪問模式,返回0,否則返回負值的錯誤碼。多數檔案系統 都將此區域設定為null,使用VFS提供的通用方法進行檢查,這種檢查操作僅僅比較索引及誒但物件中的訪問模式位是否和mask一致,比較複雜的系統, 比如支援訪問控制鏈(ACL)的檔案系統,需要使用特殊的permission()方法*/
int (*setattr) (struct dentry *, struct iattr *);
/*該函式被notify_change呼叫,在修改索引節點之後,通知發生了改變事件*/
int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
/*在通知索引節點需要從磁碟中更新時,VFS會呼叫該函式*/
int (*setxattr) (struct dentry *, const char *,
const void *, size_t, int);
/*該函式由VFS呼叫,向dentry指定的檔案設定擴充套件屬性,屬性名為name,值為value*/
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
/*該函式被VFS呼叫,向value中拷貝給定檔案的擴充套件屬性name對應的數值*/
ssize_t (*listxattr) (struct dentry *, char *, size_t);
/*該函式將特定檔案所有屬性別表拷貝到一個緩衝列表中*/
int (*removexattr) (struct dentry *, const char *);
/*該函式從給定檔案中刪除指定的屬性*/
};