1. 程式人生 > >Linux檔案系統中的inode索引結點 VFS inode

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 *);
	/*該函式從給定檔案中刪除指定的屬性*/
};