真是一個無情的搬運工
前言
本文同樣更新在私人公眾號上吨些,在此推廣一下歡迎大家關(guān)注:
公眾號會定期更新一些計算機系統(tǒng)的底層知識,爭取以最細節(jié)刁愿、最簡潔的方式幫助讀者理解系統(tǒng)的一些知識绰寞。
inode
文件儲存在硬盤上,硬盤的最小存儲單位叫做"扇區(qū)"(Sector)铣口。每個扇區(qū)儲存512字節(jié)(相當于0.5KB)滤钱。
操作系統(tǒng)讀取硬盤的時候,不會一個個扇區(qū)地讀取脑题,這樣效率太低件缸,而是一次性連續(xù)讀取多個扇區(qū),即一次性讀取一個"塊"(block)叔遂。這種由多個扇區(qū)組成的"塊"他炊,是文件存取的最小單位。"塊"的大小已艰,最常見的是4KB痊末,即連續(xù)八個 sector組成一個 block。
- inode內(nèi)容
inode包含文件的元信息哩掺,具體來說有以下內(nèi)容:
* 文件的字節(jié)數(shù)
* 文件擁有者的User ID
* 文件的Group ID
* 文件的讀凿叠、寫、執(zhí)行權(quán)限
* 文件的時間戳,共有三個:ctime指inode上一次變動的時間盒件,mtime指文件內(nèi)容上一次變動的時間蹬碧,atime指文件上一次打開的時間。
* 鏈接數(shù)履恩,即有多少文件名指向這個inode
* 文件數(shù)據(jù)block的位置
- inode大小
inode也會消耗硬盤空間锰茉,所以硬盤格式化的時候呢蔫,操作系統(tǒng)自動將硬盤分成兩個區(qū)域切心。一個是數(shù)據(jù)區(qū),存放文件數(shù)據(jù)片吊;另一個是inode區(qū)(inode table)绽昏,存放inode所包含的信息。
每個inode節(jié)點的大小俏脊,一般是128字節(jié)或256字節(jié)全谤。inode節(jié)點的總數(shù),在格式化時就給定爷贫,一般是每1KB或每2KB就設(shè)置一個inode认然。假定在一塊1GB的硬盤中,每個inode節(jié)點的大小為128字節(jié)漫萄,每1KB就設(shè)置一個inode卷员,那么inode table的大小就會達到128MB,占整塊硬盤的12.8%腾务。
讀取文件的過程
- 首先毕骡,系統(tǒng)找到這個文件名對應(yīng)的inode號碼;
- 其次岩瘦,通過inode號碼未巫,獲取inode信息;
- 最后启昧,根據(jù)inode信息叙凡,找到文件數(shù)據(jù)所在的block,讀出數(shù)據(jù)密末。
硬鏈接
一般情況下狭姨,文件名和inode號碼是"一一對應(yīng)"關(guān)系,每個inode號碼對應(yīng)一個文件名苏遥。但是饼拍,Unix/Linux系統(tǒng)允許,多個文件名指向同一個inode號碼田炭。
這意味著师抄,可以用不同的文件名訪問同樣的內(nèi)容;對文件內(nèi)容進行修改教硫,會影響到所有文件名叨吮;但是辆布,刪除一個文件名,不影響另一個文件名的訪問茶鉴。這種情況就被稱為"硬鏈接"(hard link)锋玲。
ln命令可以創(chuàng)建硬鏈接: ln 源文件 目標文件
軟鏈接
文件A和文件B的inode號碼雖然不一樣,但是文件A的內(nèi)容是文件B的路徑涵叮。讀取文件A時惭蹂,系統(tǒng)會自動將訪問者導(dǎo)向文件B。因此割粮,無論打開哪一個文件盾碗,最終讀取的都是文件B。這時舀瓢,文件A就稱為文件B的"軟鏈接"(soft link)或者"符號鏈接(symbolic link)廷雅。
這意味著,文件A依賴于文件B而存在京髓,如果刪除了文件B航缀,打開文件A就會報錯:"No such file or directory"。這是軟鏈接與硬鏈接最大的不同:文件A指向文件B的文件名堰怨,而不是文件B的inode號碼芥玉,文件B的inode"鏈接數(shù)"不會因此發(fā)生變化。
一個數(shù)據(jù)塊大小4KB诚些,則一個位圖塊可以表示410248個數(shù)據(jù)塊的使用情況飞傀,所以一個塊組中可存儲數(shù)據(jù)的大小是128MB(4 * 1024 * 8 * 4KB)
EXT-x文件系統(tǒng)中數(shù)據(jù)塊映射
Ext2/Ext3中數(shù)據(jù)塊映射方式:
于是inode
1024+ 1024^2 +1024^3+12=1,074,791,436
Extent學(xué)習(xí)記錄
Ext4中用extent樹代替了邏輯塊映射。使用extents诬烹,用一個struct ext4_extent結(jié)構(gòu)就可以映射多個數(shù)據(jù)塊砸烦,減少元數(shù)據(jù)塊的使用。
extent結(jié)構(gòu)是12個字節(jié)绞吁,所以你可以在每個inode中最多試用5個extent幢痘。然后,前12個字節(jié)是extent區(qū)(40到51字節(jié))家破,被一個extent頭結(jié)構(gòu)占用颜说,所以一個inode中實際上可以包含4個extent。
每個extent結(jié)構(gòu)中只有16個bit用來保存塊號汰聋,實際上门粪,最高位被保存了下來(最高位用來表示這個externt是“保存?預(yù)分烹困?”還是已經(jīng)初始化玄妈, 部分用于EXT4的預(yù)分配功能)。這意味著,一個extent最大只能包含2的15次方個塊拟蜻,當一個block為4k大小的時候绎签,即128MB。
128MB看起來足夠大了酝锅,但是當你的文件大于0.5G的時候诡必,這個文件就需要大于4個extent來保存整個block的索引∩Ρ猓或者爸舒,當你的文件很小,但是由很多不連續(xù)的片段組成阁谆。這些情況下碳抄,就需要用大于4個extent來組織文件愉老。
Extent并沒有非常詳細的了解场绿,后期有需要補充
Ext4中目錄項
Ext4文件系統(tǒng)中,一個目錄差不多是一個平面文件嫉入,映射任意長度的字符串到文件系統(tǒng)中的一個inode焰盗。文件系統(tǒng)中存在多個目錄項引用同一個inode——硬鏈接,這也是硬鏈接不能鏈接其他文件系統(tǒng)中的文件的原因咒林。
在目錄中并沒有存儲文件的數(shù)據(jù)信息熬拒,而只是存儲了一個類似C語言指針的東東,這個東東就是文件的inode id垫竞。而目錄中的子目錄數(shù)據(jù)和文件數(shù)據(jù)仍然是平鋪在磁盤上的澎粟。這樣,在目錄中通過這個指針就可以輕易的找到文件的數(shù)據(jù)欢瞪,而且目錄的數(shù)據(jù)和文件的數(shù)據(jù)組織也變得非常簡單活烙。
目錄本質(zhì)上也是一個文件,只不過其中存儲的數(shù)據(jù)是關(guān)于子目錄和文件的名稱信息
那么這個大數(shù)組中的元素是什么呢遣鼓?就是圖6所示的這個結(jié)構(gòu)體啸盏。從該結(jié)構(gòu)體可以看出,每一項內(nèi)容包括inode的id骑祟、該結(jié)構(gòu)體的大小回懦、文件(子目錄)名大小和文件名等信息。在檢索目錄內(nèi)容的時候次企,其實就是根據(jù)文件名獲得inode的id怯晕,然后在根據(jù)該id從inode表中獲得inode(文件)的詳細信息。
目錄查詢加速
在Ext4文件系統(tǒng)中這個索引是通過一個成為哈希樹(多叉樹)的方式實現(xiàn)的缸棵,其中Key為文件名的哈希值舟茶,而Value則是具體的數(shù)據(jù)位置(磁盤塊位置)。由于Key是有序的,因此查找非常方便稚晚,也就是可以通過文件名快速的找到ext4_dir_entry_2崇堵,然后可以找到inode信息。
超級塊
在Linux操作系統(tǒng)的文件系統(tǒng)中客燕,超級塊相當于文件系統(tǒng)的地圖鸳劳。在超級塊中保存著文件系統(tǒng)的屬性信息余素、磁盤布局和資源使用情況等信息垦写。文件系統(tǒng)通過超級塊了解磁盤的布局谢翎,查找已用和可用資源等赤屋。超級塊又相當于入口伶授,文件系統(tǒng)的操作通常從超級塊開始
struct super_block {
746 struct list_head s_list; /* Keep this first */
747 kdev_t s_dev;
748 unsigned long s_blocksize;
749 unsigned char s_blocksize_bits;
750 unsigned char s_dirt;
751 unsigned long long s_maxbytes; /* Max file size */
752 struct file_system_type *s_type;
753 struct super_operations *s_op;
754 struct dquot_operations *dq_op;
755 struct quotactl_ops *s_qcop;
756 unsigned long s_flags;
757 unsigned long s_magic;
758 struct dentry *s_root;
759 struct rw_semaphore s_umount;
760 struct semaphore s_lock;
761 int s_count;
762 atomic_t s_active;
763
764 struct list_head s_dirty; /* dirty inodes */
765 struct list_head s_locked_inodes;/* inodes being synced */
766 struct list_head s_files;
767
768 struct block_device *s_bdev;
769 struct list_head s_instances;
770 struct quota_info s_dquot; /* Diskquota specific options */
771
772 union {
773 struct minix_sb_info minix_sb;
774 struct ext2_sb_info ext2_sb;
775 struct ext3_sb_info ext3_sb;
776 struct hpfs_sb_info hpfs_sb;
777 struct ntfs_sb_info ntfs_sb;
778 struct msdos_sb_info msdos_sb;
779 struct isofs_sb_info isofs_sb;
780 struct nfs_sb_info nfs_sb;
781 struct sysv_sb_info sysv_sb;
782 struct affs_sb_info affs_sb;
783 struct ufs_sb_info ufs_sb;
784 struct efs_sb_info efs_sb;
785 struct shmem_sb_info shmem_sb;
786 struct romfs_sb_info romfs_sb;
787 struct smb_sb_info smbfs_sb;
788 struct hfs_sb_info hfs_sb;
789 struct adfs_sb_info adfs_sb;
790 struct qnx4_sb_info qnx4_sb;
791 struct reiserfs_sb_info reiserfs_sb;
792 struct bfs_sb_info bfs_sb;
793 struct udf_sb_info udf_sb;
794 struct ncp_sb_info ncpfs_sb;
795 struct usbdev_sb_info usbdevfs_sb;
796 struct jffs2_sb_info jffs2_sb;
797 struct cramfs_sb_info cramfs_sb;
798 void *generic_sbp;
799 } u;
800 /*
801 * The next field is for VFS *only*. No filesystems have any business
802 * even looking at it. You had been warned.
803 */
804 struct semaphore s_vfs_rename_sem; /* Kludge */
805
806 /* The next field is used by knfsd when converting a (inode number based)
807 * file handle into a dentry. As it builds a path in the dcache tree from
808 * the bottom up, there may for a time be a subpath of dentrys which is not
809 * connected to the main tree. This semaphore ensure that there is only ever
810 * one such free path per filesystem. Note that unconnected files (or other
811 * non-directories) are allowed, but not unconnected diretories.
812 */
813 struct semaphore s_nfsd_free_path_sem;
814 }
s_list:指向超級塊鏈表的指針涮因,這個struct list_head是很熟悉的結(jié)構(gòu)了拒迅,里面其實就是用于連接關(guān)系的prev和next字段沥寥。
內(nèi)核中的結(jié)構(gòu)處理都是有講究的(內(nèi)核協(xié)議棧中也說過)颤练,內(nèi)核單獨使用一個簡單的結(jié)構(gòu)體將所有的super_block都鏈接起來既忆,但是這個結(jié)構(gòu)不是super_block本身,因為本身數(shù)據(jù)結(jié)構(gòu)太大嗦玖,效率不高患雇,所有僅僅使用
struct
{
list_head prev;
list_head next;
}
這樣的結(jié)構(gòu)來將super_block中的s_list鏈接起來,那么遍歷到s_list之后宇挫,直接讀取super_block這么長的一個內(nèi)存塊苛吱,就可以將這個
super_block直接讀進來!這樣就很快捷方便器瘪!這也是為什么s_list必須放在第一個字段的原因翠储。
s_dev:包含該具體文件系統(tǒng)的塊設(shè)備標識符。例如橡疼,對于 /dev/hda1援所,其設(shè)備標識符為 0x301
s_blocksize:文件系統(tǒng)中數(shù)據(jù)塊大小,以字節(jié)單位
s_blocksize_bits:上面的size大小占用位數(shù)衰齐,例如512字節(jié)就是9 bits
s_dirt:臟位任斋,標識是否超級塊被修改
s_maxbytes:允許的最大的文件大小(字節(jié)數(shù))
struct file_system_type *s_type:文件系統(tǒng)類型(也就是當前這個文件系統(tǒng)屬于哪個類型?ext2還是fat32)
要區(qū)分“文件系統(tǒng)”和“文件系統(tǒng)類型”不一樣耻涛!一個文件系統(tǒng)類型下可以包括很多文件系統(tǒng)即很多的super_block废酷,后面會說!
struct super_operations *s_op:指向某個特定的具體文件系統(tǒng)的用于超級塊操作的函數(shù)集合
struct dquot_operations *dq_op:指向某個特定的具體文件系統(tǒng)用于限額操作的函數(shù)集合
struct quotactl_ops *s_qcop:用于配置磁盤限額的的方法抹缕,處理來自用戶空間的請求
s_flags:安裝標識
s_magic:區(qū)別于其他文件系統(tǒng)的標識
s_root:指向該具體文件系統(tǒng)安裝目錄的目錄項
s_umount:對超級塊讀寫時進行同步
s_lock:鎖標志位澈蟆,若置該位,則其它進程不能對該超級塊操作
s_count:對超級塊的使用計數(shù)
s_active:引用計數(shù)
s_dirty:已修改的索引節(jié)點inode形成的鏈表卓研,一個文件系統(tǒng)中有很多的inode趴俘,有些inode節(jié)點的內(nèi)容會被修改睹簇,那么會先被記錄,然后寫回磁盤寥闪。
s_locked_inodes:要進行同步的索引節(jié)點形成的鏈表
s_files:所有的已經(jīng)打開文件的鏈表太惠,這個file和實實在在的進程相關(guān)的
s_bdev:指向文件系統(tǒng)被安裝的塊設(shè)備
u:u 聯(lián)合體域包括屬于具體文件系統(tǒng)的超級塊信息
s_instances:具體的意義后來會說的!(同一類型的文件系統(tǒng)通過這個子墩將所有的super_block連接起來)
s_dquot:磁盤限額相關(guān)選項
文件系統(tǒng)中的各個數(shù)據(jù)結(jié)構(gòu)源碼:
https://www.cnblogs.com/linux-xin/p/8126999.html