http://www.ruanyifeng.com/blog/2011/12/inode.html




再來了解一下文件系統如何存取文件的
1、根據文件名,通過Directory里的對應關系,找到文件對應的Inode number
2、再根據Inode number讀取到文件的Inode table
3、再根據Inode table中的Pointer讀取到相應的Blocks




Directory:
A directory is a mapping between the human name for the file and the computer's inode number.
所以說,這個Directory不是文件,我們可以看作是文件系統中的一個屬性,只是用來關鍵文件名與Inode number。這個一定要理解好,否則后面關於硬鏈接的內容,就不容易理解了。
我在一天一點學習Linux之文件與目錄權限的基本概念中講到
第二欄表示的是有多少文件連接到inode
如果是一個文件,此時這一字段表示這個文件所具有的硬鏈接數,
如果是一個目錄,則此字段表示該目錄所含子目錄的個數。
現在是不是容易理解了?如果你還不是很明白,那么下面我們就再通過實例讓大家明白。
我們以RHEL6系統為例
在根目錄下創建一個test目錄,我們進入此目錄,進行操作。
[root@yufei test]# pwd
/test
[root@yufei test]# touch testfile
[root@yufei test]# mkdir testdir
創建實驗文件和目錄
[root@yufei test]# ls -li
total 4
977 drwxr-xr-x. 2 root root 4096 Apr 5 16:48 testdir
976 -rw-r--r--. 1 root root 0 Apr 5 16:47 testfile
查看到文件與目錄的Inode和inode count分別為
977 <-----> 2 <-----> testdir
976 <-----> 1 <-----> testfile
現在目錄的鏈接數為2,文件的鏈接數為1。為什么會這樣呢?其實很好理解。對於目錄而言,每個目錄里面肯定會有兩個特殊目錄,那就是.和..這兩個目錄,我們前面的課程中也講到,.表示當前的目錄,而..則是表示上層目錄。我們也知道,在Linux系統中,是從根來開始查找的,要想找到某個目錄,必需要先找到他的上層目錄,所以說,空目錄(嚴格的來說,不能叫空目錄)是有兩個鏈接到相應的Inode number的。作為文件很明顯,他只有一個鏈接到相應的Inode number。也不用多說,
下面我們就來看看這個鏈接數是如何改變的。
繼續上面的操作
[root@yufei test]# ln testfile testfile.hard
[root@yufei test]# ln -s testfile testfile.soft
對testfile建立一個硬鏈接和一個軟鏈接
[root@yufei test]# ls -il
total 4
977 drwxr-xr-x. 2 root root 4096 Apr 5 16:48 testdir
976 -rw-r--r--. 2 root root 0 Apr 5 16:47 testfile
976 -rw-r--r--. 2 root root 0 Apr 5 16:47 testfile.hard
978 lrwxrwxrwx. 1 root root 8 Apr 5 17:03 testfile.soft -> testfile
再查看文件和目錄的屬性,我們就發現:創建一個硬鏈接后,testfile的inode count增加了一個。而且testfile和testfile.hard這兩個的Inode number是一樣的。這個硬鏈接就是重新創建了一個文件名對應到原文件的Inode。實質就是在Directory中增加了一個新的對應關系。通過這個例子,你是不是更清楚了,這個Inode count的含義了。他就是指,一個Inode對應了多少個文件名。
下面我們再來看看硬鏈接的其他特點
[root@yufei ~]# watch -n 1 "df -i;df"
Every 1.0s: df -i;df Tue Apr 5 21:52:53 2011
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 960992 105415 855577 11% /
tmpfs 63946 1 63945 1% /dev/shm
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 15118728 2747612 11603116 20% /
tmpfs 255784 0 255784 0% /dev/shm
用上面的命令可以實時查看系統中所剩的block和inode的變化數量。
建議大家不要用deumpe2fs和tune2fs這兩個命令,如果使用他們來查看的話,將會很郁悶——你會發現,你無論怎么創建文件或對文件寫入內容,Inode和block的值都不會變,除非你每操作一次,重新啟動一次系統,而用了上面的命令,就是第秒鍾監視他們的變化情況。關於df的命令使用,大家可以自行查看幫助進行學習。當然還有du這個命令,他們都和文件系統有關。
我們再來創建一個硬鏈接
[root@yufei test]# ls -li
total 4
977 drwxr-xr-x. 2 root root 4096 Apr 5 16:48 testdir
976 -rw-r--r--. 2 root root 0 Apr 5 16:47 testfile
976 -rw-r--r--. 2 root root 0 Apr 5 16:47 testfile.hard
978 lrwxrwxrwx. 1 root root 8 Apr 5 17:03 testfile.soft -> testfile
[root@yufei test]# ln testfile testfile.hard1
[root@yufei test]# ls -li
total 4
977 drwxr-xr-x. 2 root root 4096 Apr 5 16:48 testdir
976 -rw-r--r--. 3 root root 0 Apr 5 16:47 testfile
976 -rw-r--r--. 3 root root 0 Apr 5 16:47 testfile.hard
976 -rw-r--r--. 3 root root 0 Apr 5 16:47 testfile.hard1
978 lrwxrwxrwx. 1 root root 8 Apr 5 17:03 testfile.soft -> testfile
可以再觀察一下Inode count和Inode number的對應關系。
下面再看看inodes和blocks的變化
[root@yufei ~]# watch -n 1 "df -i;df"
Every 1.0s: df -i;df Tue Apr 5 21:53:38 2011
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 960992 105415 855577 11% /
tmpfs 63946 1 63945 1% /dev/shm
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 15118728 2747612 11603116 20% /
tmpfs 255784 0 255784 0% /dev/shm
我們發現,inodes和blocks是沒有減少的,所以說,硬鏈接是不會占用磁盤的空間的。
如果說刪除硬鏈接的話,就會改變Inode count的數量。硬鏈接還有其他的兩個特性:不能跨Filesystem也不能link目錄。
下面再來看看這個軟鏈接
[root@yufei test]# ls -il testfile.soft testfile
976 -rw-r--r--. 3 root root 0 Apr 5 21:50 testfile
978 lrwxrwxrwx. 1 root root 8 Apr 5 21:52 testfile.soft -> testfile
他的Inode number和原文件不一樣。而且大小也發生了變化。可見,這個軟鏈接是重新建立了一個文件,而文件是指向到原文件,而不是指向原Inode。當然他會占用掉 inode 與 block。當我們刪除了源文件后,鏈接文件不能獨立存在,雖然仍保留文件名,但我們卻不能查看軟鏈接文件的內容了。但軟鏈接是可以跨文件系統,而且是可以鏈接目錄。他就相當於windows系統下的快捷方式一樣。通過這個特性,我們可以通過軟鏈接解決某個分區inode conut不足的問題(軟鏈接到另一個inode count足夠多的分區)。
接下來,我們再來分析一下復制文件、移動文件和刪除文件對inode的影響
[root@yufei ~]# watch -n 1 "df -i;df"
Every 1.0s: df -i;df Tue Apr 5 21:57:38 2011
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 960992 105415 855577 11% /
tmpfs 63946 1 63945 1% /dev/shm
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 15118728 2747612 11603116 20% /
tmpfs 255784 0 255784 0% /dev/shm
[root@yufei test]# ls -li
total 4
977 drwxr-xr-x. 2 root root 4096 Apr 5 16:48 testdir
976 -rw-r--r--. 3 root root 0 Apr 5 18:54 testfile
976 -rw-r--r--. 3 root root 0 Apr 5 18:54 testfile.hard
976 -rw-r--r--. 3 root root 0 Apr 5 18:54 testfile.hard1
978 lrwxrwxrwx. 1 root root 8 Apr 5 17:03 testfile.soft -> testfile
我們先記錄以上的信息
先看復制文件的情況
[root@yufei test]# cp testfile testfile.cp
[root@yufei test]# ls -li
976 -rw-r--r--. 3 root root 0 Apr 5 21:50 testfile
979 -rw-r--r--. 1 root root 0 Apr 5 21:58 testfile.cp
我們只對比這兩個文件,發現Inode number不一樣,我們再來看看inodes和blocks的剩余情況
Every 1.0s: df -i;df Tue Apr 5 22:02:49 2011
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 960992 105416 855576 11% /
tmpfs 63946 1 63945 1% /dev/shm
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 15118728 2747620 11603108 20% /
tmpfs 255784 0 255784 0% /dev/shm
發現inodes減少了一個,而blocks也少了,這就說明,復制文件是創建文件,並占Inode和Block的。
文件創建過程是:先查找一個空的Inode,寫入新的Inode table,創建Directory,對應文件名,向block中寫入文件內容
關於移動文件和刪除文件的實驗,大家可以自己動手來實踐吧。我直接給出相應的說明。
移動文件,他分兩種情況:
在同一個文件系統中移動文件時
創建一個新的文件名和Inode的對應關系(也就是在Directory中寫入信息),然后在Directory中刪除舊的信息,更新CTIME,其他的信息如Inode等等均無任何影響
在不同文件系統移動文件時
先查找一個空的Inode,寫入新的Inode table,創建Directory中的對應關系,向block中寫入文件內容,同時還會更改CTIME。
刪除文件
他實質上就是減少link count,當link count為0時,就表示這個Inode可以使用,並把Block標記為可以寫,但並沒有清除Block里面數據,除非是有新的數據需要用到這個block。
最后我們來做個總結:
2、更為准確的來說,一個文件只對應一個Inode。因為硬鏈接其實不是創建新文件,只是在Directory中寫入了新的對應關系而已。
3、當我們刪除文件的時候,只是把Inode標記為可用,文件在block中的內容是沒有被清除的,只有在有新的文件需要占用block的時候,才會被覆蓋。
、*
*索引節點對象由inode結構體表示,定義文件在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 *);
/*該函數從給定文件中刪除指定的屬性*/
};