真是一個(gè)無(wú)情的搬運(yùn)工
前言
本文同樣更新在私人公眾號(hào)上,在此推廣一下歡迎大家關(guān)注:
公眾號(hào)會(huì)定期更新一些計(jì)算機(jī)系統(tǒng)的底層知識(shí),爭(zhēng)取以最細(xì)節(jié)、最簡(jiǎn)潔的方式幫助讀者理解系統(tǒng)的一些知識(shí)。

inode
文件儲(chǔ)存在硬盤上,硬盤的最小存儲(chǔ)單位叫做"扇區(qū)"(Sector)。每個(gè)扇區(qū)儲(chǔ)存512字節(jié)(相當(dāng)于0.5KB)。
操作系統(tǒng)讀取硬盤的時(shí)候,不會(huì)一個(gè)個(gè)扇區(qū)地讀取,這樣效率太低,而是一次性連續(xù)讀取多個(gè)扇區(qū),即一次性讀取一個(gè)"塊"(block)。這種由多個(gè)扇區(qū)組成的"塊",是文件存取的最小單位。"塊"的大小,最常見(jiàn)的是4KB,即連續(xù)八個(gè) sector組成一個(gè) block。
- inode內(nèi)容
inode包含文件的元信息,具體來(lái)說(shuō)有以下內(nèi)容:
* 文件的字節(jié)數(shù)
* 文件擁有者的User ID
* 文件的Group ID
* 文件的讀、寫、執(zhí)行權(quán)限
* 文件的時(shí)間戳,共有三個(gè):ctime指inode上一次變動(dòng)的時(shí)間,mtime指文件內(nèi)容上一次變動(dòng)的時(shí)間,atime指文件上一次打開(kāi)的時(shí)間。
* 鏈接數(shù),即有多少文件名指向這個(gè)inode
* 文件數(shù)據(jù)block的位置
- inode大小
inode也會(huì)消耗硬盤空間,所以硬盤格式化的時(shí)候,操作系統(tǒng)自動(dòng)將硬盤分成兩個(gè)區(qū)域。一個(gè)是數(shù)據(jù)區(qū),存放文件數(shù)據(jù);另一個(gè)是inode區(qū)(inode table),存放inode所包含的信息。
每個(gè)inode節(jié)點(diǎn)的大小,一般是128字節(jié)或256字節(jié)。inode節(jié)點(diǎn)的總數(shù),在格式化時(shí)就給定,一般是每1KB或每2KB就設(shè)置一個(gè)inode。假定在一塊1GB的硬盤中,每個(gè)inode節(jié)點(diǎn)的大小為128字節(jié),每1KB就設(shè)置一個(gè)inode,那么inode table的大小就會(huì)達(dá)到128MB,占整塊硬盤的12.8%。
讀取文件的過(guò)程
- 首先,系統(tǒng)找到這個(gè)文件名對(duì)應(yīng)的inode號(hào)碼;
- 其次,通過(guò)inode號(hào)碼,獲取inode信息;
- 最后,根據(jù)inode信息,找到文件數(shù)據(jù)所在的block,讀出數(shù)據(jù)。
硬鏈接
一般情況下,文件名和inode號(hào)碼是"一一對(duì)應(yīng)"關(guān)系,每個(gè)inode號(hào)碼對(duì)應(yīng)一個(gè)文件名。但是,Unix/Linux系統(tǒng)允許,多個(gè)文件名指向同一個(gè)inode號(hào)碼。
這意味著,可以用不同的文件名訪問(wèn)同樣的內(nèi)容;對(duì)文件內(nèi)容進(jìn)行修改,會(huì)影響到所有文件名;但是,刪除一個(gè)文件名,不影響另一個(gè)文件名的訪問(wèn)。這種情況就被稱為"硬鏈接"(hard link)。
ln命令可以創(chuàng)建硬鏈接: ln 源文件 目標(biāo)文件
軟鏈接
文件A和文件B的inode號(hào)碼雖然不一樣,但是文件A的內(nèi)容是文件B的路徑。讀取文件A時(shí),系統(tǒng)會(huì)自動(dòng)將訪問(wèn)者導(dǎo)向文件B。因此,無(wú)論打開(kāi)哪一個(gè)文件,最終讀取的都是文件B。這時(shí),文件A就稱為文件B的"軟鏈接"(soft link)或者"符號(hào)鏈接(symbolic link)。
這意味著,文件A依賴于文件B而存在,如果刪除了文件B,打開(kāi)文件A就會(huì)報(bào)錯(cuò):"No such file or directory"。這是軟鏈接與硬鏈接最大的不同:文件A指向文件B的文件名,而不是文件B的inode號(hào)碼,文件B的inode"鏈接數(shù)"不會(huì)因此發(fā)生變化。
一個(gè)數(shù)據(jù)塊大小4KB,則一個(gè)位圖塊可以表示410248個(gè)數(shù)據(jù)塊的使用情況,所以一個(gè)塊組中可存儲(chǔ)數(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樹(shù)代替了邏輯塊映射。使用extents,用一個(gè)struct ext4_extent結(jié)構(gòu)就可以映射多個(gè)數(shù)據(jù)塊,減少元數(shù)據(jù)塊的使用。
extent結(jié)構(gòu)是12個(gè)字節(jié),所以你可以在每個(gè)inode中最多試用5個(gè)extent。然后,前12個(gè)字節(jié)是extent區(qū)(40到51字節(jié)),被一個(gè)extent頭結(jié)構(gòu)占用,所以一個(gè)inode中實(shí)際上可以包含4個(gè)extent。
每個(gè)extent結(jié)構(gòu)中只有16個(gè)bit用來(lái)保存塊號(hào),實(shí)際上,最高位被保存了下來(lái)(最高位用來(lái)表示這個(gè)externt是“保存?預(yù)分?”還是已經(jīng)初始化, 部分用于EXT4的預(yù)分配功能)。這意味著,一個(gè)extent最大只能包含2的15次方個(gè)塊,當(dāng)一個(gè)block為4k大小的時(shí)候,即128MB。
128MB看起來(lái)足夠大了,但是當(dāng)你的文件大于0.5G的時(shí)候,這個(gè)文件就需要大于4個(gè)extent來(lái)保存整個(gè)block的索引。或者,當(dāng)你的文件很小,但是由很多不連續(xù)的片段組成。這些情況下,就需要用大于4個(gè)extent來(lái)組織文件。
Extent并沒(méi)有非常詳細(xì)的了解,后期有需要補(bǔ)充
Ext4中目錄項(xiàng)
Ext4文件系統(tǒng)中,一個(gè)目錄差不多是一個(gè)平面文件,映射任意長(zhǎng)度的字符串到文件系統(tǒng)中的一個(gè)inode。文件系統(tǒng)中存在多個(gè)目錄項(xiàng)引用同一個(gè)inode——硬鏈接,這也是硬鏈接不能鏈接其他文件系統(tǒng)中的文件的原因。
在目錄中并沒(méi)有存儲(chǔ)文件的數(shù)據(jù)信息,而只是存儲(chǔ)了一個(gè)類似C語(yǔ)言指針的東東,這個(gè)東東就是文件的inode id。而目錄中的子目錄數(shù)據(jù)和文件數(shù)據(jù)仍然是平鋪在磁盤上的。這樣,在目錄中通過(guò)這個(gè)指針就可以輕易的找到文件的數(shù)據(jù),而且目錄的數(shù)據(jù)和文件的數(shù)據(jù)組織也變得非常簡(jiǎn)單。

目錄本質(zhì)上也是一個(gè)文件,只不過(guò)其中存儲(chǔ)的數(shù)據(jù)是關(guān)于子目錄和文件的名稱信息
那么這個(gè)大數(shù)組中的元素是什么呢?就是圖6所示的這個(gè)結(jié)構(gòu)體。從該結(jié)構(gòu)體可以看出,每一項(xiàng)內(nèi)容包括inode的id、該結(jié)構(gòu)體的大小、文件(子目錄)名大小和文件名等信息。在檢索目錄內(nèi)容的時(shí)候,其實(shí)就是根據(jù)文件名獲得inode的id,然后在根據(jù)該id從inode表中獲得inode(文件)的詳細(xì)信息。

目錄查詢加速
在Ext4文件系統(tǒng)中這個(gè)索引是通過(guò)一個(gè)成為哈希樹(shù)(多叉樹(shù))的方式實(shí)現(xiàn)的,其中Key為文件名的哈希值,而Value則是具體的數(shù)據(jù)位置(磁盤塊位置)。由于Key是有序的,因此查找非常方便,也就是可以通過(guò)文件名快速的找到ext4_dir_entry_2,然后可以找到inode信息。


超級(jí)塊
在Linux操作系統(tǒng)的文件系統(tǒng)中,超級(jí)塊相當(dāng)于文件系統(tǒng)的地圖。在超級(jí)塊中保存著文件系統(tǒng)的屬性信息、磁盤布局和資源使用情況等信息。文件系統(tǒng)通過(guò)超級(jí)塊了解磁盤的布局,查找已用和可用資源等。超級(jí)塊又相當(dāng)于入口,文件系統(tǒng)的操作通常從超級(jí)塊開(kāi)始
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:指向超級(jí)塊鏈表的指針,這個(gè)struct list_head是很熟悉的結(jié)構(gòu)了,里面其實(shí)就是用于連接關(guān)系的prev和next字段。
內(nèi)核中的結(jié)構(gòu)處理都是有講究的(內(nèi)核協(xié)議棧中也說(shuō)過(guò)),內(nèi)核單獨(dú)使用一個(gè)簡(jiǎn)單的結(jié)構(gòu)體將所有的super_block都鏈接起來(lái),但是這個(gè)結(jié)構(gòu)不是super_block本身,因?yàn)楸旧頂?shù)據(jù)結(jié)構(gòu)太大,效率不高,所有僅僅使用
struct
{
list_head prev;
list_head next;
}
這樣的結(jié)構(gòu)來(lái)將super_block中的s_list鏈接起來(lái),那么遍歷到s_list之后,直接讀取super_block這么長(zhǎng)的一個(gè)內(nèi)存塊,就可以將這個(gè)
super_block直接讀進(jìn)來(lái)!這樣就很快捷方便!這也是為什么s_list必須放在第一個(gè)字段的原因。
s_dev:包含該具體文件系統(tǒng)的塊設(shè)備標(biāo)識(shí)符。例如,對(duì)于 /dev/hda1,其設(shè)備標(biāo)識(shí)符為 0x301
s_blocksize:文件系統(tǒng)中數(shù)據(jù)塊大小,以字節(jié)單位
s_blocksize_bits:上面的size大小占用位數(shù),例如512字節(jié)就是9 bits
s_dirt:臟位,標(biāo)識(shí)是否超級(jí)塊被修改
s_maxbytes:允許的最大的文件大小(字節(jié)數(shù))
struct file_system_type *s_type:文件系統(tǒng)類型(也就是當(dāng)前這個(gè)文件系統(tǒng)屬于哪個(gè)類型?ext2還是fat32)
要區(qū)分“文件系統(tǒng)”和“文件系統(tǒng)類型”不一樣!一個(gè)文件系統(tǒng)類型下可以包括很多文件系統(tǒng)即很多的super_block,后面會(huì)說(shuō)!
struct super_operations *s_op:指向某個(gè)特定的具體文件系統(tǒng)的用于超級(jí)塊操作的函數(shù)集合
struct dquot_operations *dq_op:指向某個(gè)特定的具體文件系統(tǒng)用于限額操作的函數(shù)集合
struct quotactl_ops *s_qcop:用于配置磁盤限額的的方法,處理來(lái)自用戶空間的請(qǐng)求
s_flags:安裝標(biāo)識(shí)
s_magic:區(qū)別于其他文件系統(tǒng)的標(biāo)識(shí)
s_root:指向該具體文件系統(tǒng)安裝目錄的目錄項(xiàng)
s_umount:對(duì)超級(jí)塊讀寫時(shí)進(jìn)行同步
s_lock:鎖標(biāo)志位,若置該位,則其它進(jìn)程不能對(duì)該超級(jí)塊操作
s_count:對(duì)超級(jí)塊的使用計(jì)數(shù)
s_active:引用計(jì)數(shù)
s_dirty:已修改的索引節(jié)點(diǎn)inode形成的鏈表,一個(gè)文件系統(tǒng)中有很多的inode,有些inode節(jié)點(diǎn)的內(nèi)容會(huì)被修改,那么會(huì)先被記錄,然后寫回磁盤。
s_locked_inodes:要進(jìn)行同步的索引節(jié)點(diǎn)形成的鏈表
s_files:所有的已經(jīng)打開(kāi)文件的鏈表,這個(gè)file和實(shí)實(shí)在在的進(jìn)程相關(guān)的
s_bdev:指向文件系統(tǒng)被安裝的塊設(shè)備
u:u 聯(lián)合體域包括屬于具體文件系統(tǒng)的超級(jí)塊信息
s_instances:具體的意義后來(lái)會(huì)說(shuō)的!(同一類型的文件系統(tǒng)通過(guò)這個(gè)子墩將所有的super_block連接起來(lái))
s_dquot:磁盤限額相關(guān)選項(xiàng)
文件系統(tǒng)中的各個(gè)數(shù)據(jù)結(jié)構(gòu)源碼:
https://www.cnblogs.com/linux-xin/p/8126999.html