简单文件系统的实现_300来行代码带你实现一个能跑的最小Linux文件系统
Linux作為一個類UNIX系統,其文件系統保留了原始UNIX文件系統的表象形式,它看起來是這個樣子:
t@name-VirtualBox:/# lsbin boot cdrom dev etc home lib lib64 lost+found media mnt opt proc root run sbin snap srv sys tmp usr var它其實是一棵目錄樹(沒有畫全):
然而,雖然所有的UNIX系統以及類UNIX系統的文件系統看起來一樣,但是它們的實現卻是不盡相同。
作為普通用戶,了解文件系統的基本操作就夠了;作為應用開發人員,了解文件系統的POSIX接口足矣,但是作為一個對操作系統有著濃厚興趣的愛好者而言,自己可能就是一個新的文件系統的潛在實現者,所以必須一窺究竟,看看如此外觀的文件系統到底是如何實現的。
網上已經有了很多關于UNIX/Linux文件系統實現的資源,但是無一例外,都太復雜了,除了整體的源碼分析外,幾乎就是針對某個特定文件系統的詳解了,如此復雜的這些對于初涉該領域的滿腔熱情者無疑是一盆冷水,很多人因此望而卻步。
- ...
- mount機制是如何實現的?
- inode是如何分配的?磁盤inode和內存inode有什么區別?
- dentry緩存是怎么回事?是如何管理的?
- pagecache是什么?radix樹如何管理文件數據緩存?
- cache和buffer的區別又是什么?
- ...
幾乎所有的關于Linux文件系統實現的資源都在用不同的語言解釋上面的這些問題,這很容易陷入細節的泥潭。
本文以Linux內核為例,用一種稍微不同的方式去描述文件系統的實現。嗯,我會分3個部分來介紹Linux內核的文件系統:
本文中,我會通過一個實實在在的文件系統實現的例子,試圖闡述 實現一個文件系統,哪些是必須的,哪些不是必須的。 這是一個任務驅動的過程,從簡單的例子開始。
讀過本文之后,相信會對Linux文件系統的實現有一個總體上的宏觀把握,然后再去反復推敲上述的細節問題,重讀網上的那些經典資源,相信會事半功倍。
Linux文件系統在不同視角下的樣子
當然,在給出最簡單的tinyfs實現之前,還是會有一個總體的介紹。
如果我們把本文最初描述的那個在幾乎所有UNIX/類UNIX系統中長的一模一樣的文件系統表面刨開,在Linux內核中,文初的那棵樹其實它長下面的樣子(其實在大多數類UNIX系統中,它們長得都差不多):
【這張圖基于我一張手繪圖修改而成】
我們看到,Linux系統的文件目錄樹就是靠上圖中的這一系列的鏈表穿針引線給串在一起的,就像縫制一件衣服一樣,最終的成衣就是我們看到的Linux系統目錄樹,而縫制這件成衣的線以及指導走線的規則便是VFS本身了。
現在只要記住兩個重要鏈表:
然后讀完本文之后再去結合代碼深入分析它們是如何串起整個文件系統的。
VFS之所有可以將機制大相徑庭的完全不同的文件系統對外統一成一個樣子,完全就是依靠了它的統一的對POSIX文件調用的接口,該接口的結構看上去是下面的樣子:
注意上圖最下面的那個橢圓,如果要實現一個文件系統,這個橢圓里的東西是關鍵,它完成了穿針引線的大部分工作。
現在讓我們縱向地看一下一個完整的文件系統實現都包括什么,我指的是從POSIX系統調用開始,一直到數據落盤。Linux內核關于文件系統IO,完整的視圖如下所示:
注意VFS提供的三類接口:
- 和POSIX系統調用的接口 即實現open/read/write的操作的接口。
- 和底層介質的接口 即下接塊設備層的接口。
- 如何管理自身 即何時以及如何操作VFS數據結構inode,dentry,mount等對象。
一個文件系統如果能實現上面三類接口,那它就是個完整的文件系統了。
我們恰好可以從設計并實現一個最基本的這樣的文件系統開始。一個基本的文件系統,其著重點在于上圖中紅色的部分,而其它部分則不是必不可少的,但是卻是讓該文件系統變得優秀(而不僅僅是可用)所必須的。
實現一個很小但能跑的文件系統
為什么要實現這么一個文件系統,難道沒人已經做了這個工作嗎?做這個工作的意義何在?
原因如下:
- 我沒有找到現成的比較完整且炫酷的作品。 當然有人寫這種文件系統練手,但是看下來要么就是使用了libfs.c里封裝好的接口,要么就是沒有自己設計文件系統的底層存儲格式。
- 下班的班車在路上堵了一個多小時,無聊擼會兒代碼。
然而確實,我沒有找到簡單的 最小文件系統 實現,也許你會說Linux內核自帶的ramfs難道不就是一個現成的嗎?的確算一個,但它有兩個問題導致你無法領略實現一個文件系統的全過程,注意,我說的可是全過程:
為了 追求完整, 如果你把如何組織一塊內核作為ramfs的底層介質這部分代碼全部看完,如果你把libfs.c里的庫實現全部看完,我想ramfs也就不算一個 足夠簡單 的文件系統實例了。
看到了么?要想代碼簡單,你就不得不使用libfs.c里的現成的例程,這將損失你實現一個文件系統的完整性體驗,反之,要想完整實現一個文件系統,你可能不得不自己寫大量的代碼,這卻并不簡單。
如何既完備,又足夠簡單呢?
對于我這種編程水平渣渣的內核愛好者而言,如何在堵車的一個多小時內完成一個可以編譯通過的文件系統(我承認完全能跑是我回到家后又調試了一個多小時才完成的...),這對于我而言,是一個挑戰,但我要試一試,沒想到就成功了。所以才有了今天的分享。
我從最底層的介質結構的設計開始。
我并沒有真實的硬件介質,也并不打算編寫專門的 格式化程序 去格式化一塊內存區域,所以我直接用大數組定義一塊內存,它便是我的模擬介質了,我的tinyfs的文件格式如下:
// tinyfs.h#define MAXLEN 8#define MAX_FILES 32#define MAX_BLOCKSIZE 512// 定義每一個目錄項的格式struct dir_entry { char filename[MAXLEN]; uint8_t idx;};// 定義每一個文件的格式。struct file_blk { uint8_t busy; mode_t mode; uint8_t idx; union { uint8_t file_size; uint8_t dir_children; }; char data[0];};// OK,下面的block數組所占據的連續內存就是我的tinyfs的介質,每一個元素代表一個文件。// struct file_blk block[MAX_FILES+1];這個文件系統的格式非常的Low:
之所以這么Low是因為它只是一個開始, 當這個文件系統實現并且能跑之后,你會發現它因為Low而帶來的不足和一些代價,而彌補這些不足正好是優化的動機,帶著你逐步實現一個更加不Low的文件系統,在實現的過程中,你會窺見并掌握Linux內核文件系統的全貌和細節。 完美的學習過程,OK!
下面是代碼:
// tinyfs.c#include #include #include #include #include "tinyfs.h"struct file_blk block[MAX_FILES+1];int curr_count = 0; // 我勒個去,竟然使用了全局變量!// 獲得一個尚未使用的文件塊,保存新創建的文件或者目錄static int get_block(void){ int i; // 就是一個遍歷,但實現快速。 for (i = 2; i < MAX_FILES; i++) { if (!block[i].busy) { block[i].busy = 1; return i; } } return -1;}static struct inode_operations tinyfs_inode_ops;// 讀取目錄的實現static int tinyfs_readdir(struct file *filp, void *dirent, filldir_t filldir){ loff_t pos; struct file_blk *blk; struct dir_entry *entry; int i; pos = filp->f_pos; if (pos) return 0; blk = (struct file_blk *)filp->f_dentry->d_inode->i_private; if (!S_ISDIR(blk->mode)) { return -ENOTDIR; } // 循環獲取一個目錄的所有文件的文件名 entry = (struct dir_entry *)&blk->data[0]; for (i = 0; i < blk->dir_children; i++) { filldir(dirent, entry[i].filename, MAXLEN, pos, entry[i].idx, DT_UNKNOWN); filp->f_pos += sizeof(struct dir_entry); pos += sizeof(struct dir_entry); } return 0;}// read實現ssize_t tinyfs_read(struct file * filp, char __user * buf, size_t len, loff_t *ppos){ struct file_blk *blk; char *buffer; blk = (struct file_blk *)filp->f_path.dentry->d_inode->i_private; if (*ppos >= blk->file_size) return 0; buffer = (char *)&blk->data[0]; len = min((size_t) blk->file_size, len); if (copy_to_user(buf, buffer, len)) { return -EFAULT; } *ppos += len; return len;}// write實現ssize_t tinyfs_write(struct file * filp, const char __user * buf, size_t len, loff_t * ppos){ struct file_blk *blk; char *buffer; blk = filp->f_path.dentry->d_inode->i_private; buffer = (char *)&blk->data[0]; buffer += *ppos; if (copy_from_user(buffer, buf, len)) { return -EFAULT; } *ppos += len; blk->file_size = *ppos; return len;}const struct file_operations tinyfs_file_operations = { .read = tinyfs_read, .write = tinyfs_write,};const struct file_operations tinyfs_dir_operations = { .owner = THIS_MODULE, .readdir = tinyfs_readdir,};// 創建文件的實現static int tinyfs_do_create(struct inode *dir, struct dentry *dentry, umode_t mode){ struct inode *inode; struct super_block *sb; struct dir_entry *entry; struct file_blk *blk, *pblk; int idx; sb = dir->i_sb; if (curr_count >= MAX_FILES) { return -ENOSPC; } if (!S_ISDIR(mode) && !S_ISREG(mode)) { return -EINVAL; } inode = new_inode(sb); if (!inode) { return -ENOMEM; } inode->i_sb = sb; inode->i_op = &tinyfs_inode_ops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; idx = get_block(); // 獲取一個空閑的文件塊保存新文件 blk = &block[idx]; inode->i_ino = idx; blk->mode = mode; curr_count ++; if (S_ISDIR(mode)) { blk->dir_children = 0; inode->i_fop = &tinyfs_dir_operations; } else if (S_ISREG(mode)) { blk->file_size = 0; inode->i_fop = &tinyfs_file_operations; } inode->i_private = blk; pblk = (struct file_blk *)dir->i_private; entry = (struct dir_entry *)&pblk->data[0]; entry += pblk->dir_children; pblk->dir_children ++; entry->idx = idx; strcpy(entry->filename, dentry->d_name.name); // VFS穿針引線的關鍵步驟,將VFS的inode鏈接到鏈表 inode_init_owner(inode, dir, mode); d_add(dentry, inode); return 0;}static int tinyfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode){ return tinyfs_do_create(dir, dentry, S_IFDIR | mode);}static int tinyfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl){ return tinyfs_do_create(dir, dentry, mode);}static struct inode *tinyfs_iget(struct super_block *sb, int idx){ struct inode *inode; struct file_blk *blk; inode = new_inode(sb); inode->i_ino = idx; inode->i_sb = sb; inode->i_op = &tinyfs_inode_ops; blk = &block[idx]; if (S_ISDIR(blk->mode)) inode->i_fop = &tinyfs_dir_operations; else if (S_ISREG(blk->mode)) inode->i_fop = &tinyfs_file_operations; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_private = blk; return inode;}struct dentry *tinyfs_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags){ struct super_block *sb = parent_inode->i_sb; struct file_blk *blk; struct dir_entry *entry; int i; blk = (struct file_blk *)parent_inode->i_private; entry = (struct dir_entry *)&blk->data[0]; for (i = 0; i < blk->dir_children; i++) { if (!strcmp(entry[i].filename, child_dentry->d_name.name)) { struct inode *inode = tinyfs_iget(sb, entry[i].idx); struct file_blk *inner = (struct file_blk *)inode->i_private; inode_init_owner(inode, parent_inode, inner->mode); d_add(child_dentry, inode); return NULL; } } return NULL;}int tinyfs_rmdir(struct inode *dir, struct dentry *dentry){ struct inode *inode = dentry->d_inode; struct file_blk *blk = (struct file_blk *)inode->i_private; blk->busy = 0; return simple_rmdir(dir, dentry);}int tinyfs_unlink(struct inode *dir, struct dentry *dentry){ int i; struct inode *inode = dentry->d_inode; struct file_blk *blk = (struct file_blk *)inode->i_private; struct file_blk *pblk = (struct file_blk *)dir->i_private; struct dir_entry *entry; // 更新其上層目錄 entry = (struct dir_entry *)&pblk->data[0]; for (i = 0; i < pblk->dir_children; i++) { if (!strcmp(entry[i].filename, dentry->d_name.name)) { int j; for (j = i; j < pblk->dir_children - 1; j++) { memcpy(&entry[j], &entry[j+1], sizeof(struct dir_entry)); } pblk->dir_children --; break; } } blk->busy = 0; return simple_unlink(dir, dentry);}static struct inode_operations tinyfs_inode_ops = { .create = tinyfs_create, .lookup = tinyfs_lookup, .mkdir = tinyfs_mkdir, .rmdir = tinyfs_rmdir, .unlink = tinyfs_unlink,};int tinyfs_fill_super(struct super_block *sb, void *data, int silent){ struct inode *root_inode; int mode = S_IFDIR; root_inode = new_inode(sb); root_inode->i_ino = 1; inode_init_owner(root_inode, NULL, mode); root_inode->i_sb = sb; root_inode->i_op = &tinyfs_inode_ops; root_inode->i_fop = &tinyfs_dir_operations; root_inode->i_atime = root_inode->i_mtime = root_inode->i_ctime = CURRENT_TIME; block[1].mode = mode; block[1].dir_children = 0; block[1].idx = 1; block[1].busy = 1; root_inode->i_private = &block[1]; sb->s_root = d_make_root(root_inode); curr_count ++; return 0;}static struct dentry *tinyfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data){ return mount_nodev(fs_type, flags, data, tinyfs_fill_super);}static void tinyfs_kill_superblock(struct super_block *sb){ kill_anon_super(sb);}struct file_system_type tinyfs_fs_type = { .owner = THIS_MODULE, .name = "tinyfs總結
以上是生活随笔為你收集整理的简单文件系统的实现_300来行代码带你实现一个能跑的最小Linux文件系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 混动车EV模式发动机会工作,是怎么回事,
- 下一篇: 义乌中心医院有充电瓶车的吗?