動(dòng)手寫一個(gè)簡單的文件系統(tǒng).md

原帖地址:
https://zhangshurong.github.io/2018/03/25/%E5%8A%A8%E6%89%8B%E5%86%99%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/
項(xiàng)目地址:https://github.com/ZhangShurong/HUST_OS_fs_experiment


title: 動(dòng)手寫一個(gè)簡單的文件系統(tǒng)
date: 2018-03-25 16:53:50
categories: "Linux"
tags:

  • Linux
  • Kernel
  • FileSystem

總體設(shè)計(jì)

本文件系統(tǒng)的磁盤結(jié)構(gòu)參考minix的文件系統(tǒng)實(shí)現(xiàn)。但是自舉塊(或稱引導(dǎo)塊)中沒有數(shù)據(jù)潜叛。切不采用二級(jí)或者多級(jí)索引,其結(jié)構(gòu)如下:

|Dummy Block|Super Block|IMap|BMap|Inode Table|Data blocks|

其中每個(gè)塊的大小定義為4096bytes,每個(gè)Inode含有10個(gè)塊所以單個(gè)文件最大為40KB,支持的最小磁盤大小為24K木柬。以下詳細(xì)闡述文件系統(tǒng)中所需要的三個(gè)基本數(shù)據(jù)結(jié)構(gòu)或舞。

1. 超級(jí)塊結(jié)構(gòu):

struct HUST_fs_super_block {
    uint64_t version;
    uint64_t magic;
    uint64_t block_size;
    uint64_t inodes_count;
    uint64_t free_blocks;
    uint64_t blocks_count;
    uint64_t bmap_block;
    uint64_t imap_block;
    uint64_t inode_table_block;
    uint64_t data_block_number;
    char padding[4016];
};

超級(jí)塊中的padding數(shù)組,是為了使超級(jí)塊的大小為4096bytes勇婴,以簡化后期的工作;“Magic”為1314522嘱腥;indoe_count記錄文件系統(tǒng)所支持的inode個(gè)數(shù)耕渴,這個(gè)值在格式化時(shí)就已經(jīng)計(jì)算并寫入超級(jí)塊了。Bmap_block記錄著bmap開始的數(shù)據(jù)塊索引,imap_block齿兔,inode_table_block和data_block_number同理橱脸,記錄索引是為了簡化文件塊的定位操作。

2. HUST_inode結(jié)構(gòu)

struct HUST_inode {  
    mode_t mode; //sizeof(mode_t) is 4  
    uint64_t inode_no;  
    uint64_t blocks;  
    uint64_t block[HUST_N_BLOCKS];  
    union {  
        uint64_t file_size;  
        uint64_t dir_children_count;  
    };  
    int32_t i_uid;   
    int32_t i_gid;  
    int32_t i_nlink;  
    int64_t i_atime;  
    int64_t i_mtime;  
    int64_t i_ctime;  
    char padding[112];  
};  

HUST_inode對(duì)應(yīng)著磁盤上的inode結(jié)構(gòu)分苇,在后文會(huì)描述它是如何轉(zhuǎn)換為VFS中的inode的添诉。在上述結(jié)構(gòu)體中,mode代表該inode是文件還是目錄医寿,blocks代表該inode的大欣父啊(所占?jí)K的數(shù)目),i_uid和i_gid用于后面的多用戶管理糟红。Padding數(shù)組是為了讓HUST_inode結(jié)構(gòu)體能夠被4096整除艾帐;宏HUST_N_BLOCK被定義為10,意味著每個(gè)文件(目錄)最大的大小為10個(gè)塊盆偿;block數(shù)組存儲(chǔ)著每個(gè)塊的索引柒爸,用于定位文件。

3. 文件系統(tǒng)的目錄結(jié)構(gòu)

struct HUST_dir_record {  
    char filename[HUST_FILENAME_MAX_LEN];  
    uint64_t inode_no;  
};

文件記錄是為了儲(chǔ)存目錄項(xiàng)事扭,其中HUST_FILENAME_MAX_LEN定義為256也就是說捎稚,文件名最大長度256。
編寫文件系統(tǒng)除了設(shè)計(jì)文件系統(tǒng)的磁盤結(jié)構(gòu)求橄,定義文件系統(tǒng)支持的操作也是十分重要的今野,這個(gè)直接影響了文件系統(tǒng)的功能。本文件系統(tǒng)支持基本的文件的增刪改查罐农,多用戶等功能条霜,但是不支持文件的移動(dòng),軟硬鏈接等操作涵亏。

mkfs的實(shí)現(xiàn)

要使用這個(gè)文件系統(tǒng)宰睡,必須首先創(chuàng)建一個(gè)符合磁盤布局的映像文件,所以我們需要實(shí)現(xiàn)一個(gè)格式化程序气筋,這個(gè)程序按照慣例叫做mkfs拆内。本節(jié)詳細(xì)描述mkfs的實(shí)現(xiàn)。
mkfs的作用是將一個(gè)文件改寫成對(duì)應(yīng)于我們文件系統(tǒng)的結(jié)構(gòu)宠默,其主要功能點(diǎn)為寫入超級(jí)塊麸恍,寫入imap,bmap,寫入inode table搀矫,以及創(chuàng)建一個(gè)根目錄和測(cè)試文件抹沪。
超級(jí)塊包含了文件系統(tǒng)的基本信息刻肄,其信息在上文中有詳細(xì)描述。寫入超級(jí)塊信息采够,需要計(jì)算整個(gè)磁盤的大小肄方,然后計(jì)算imap,bmap以及inode table的大小蹬癌,這樣才能確定各個(gè)區(qū)域在磁盤中的位置权她。這些工作都是在init_disk這個(gè)函數(shù)中完成的∈判剑基本邏輯為讀取需要格式化的文件大小隅要,計(jì)算出整個(gè)磁盤中的塊的個(gè)數(shù),簡單的將塊的個(gè)數(shù)與inode的個(gè)數(shù)等同起來董济;然后通過塊數(shù)以及inode個(gè)數(shù)計(jì)算imap和bmap的大小步清。其中bmap的大小如下(imap大小計(jì)算公式與bmap一致):
$$
bmapsize = blockcount/ HUST_BLOCKSIZE * 8
$$
關(guān)鍵代碼如下:

static int init_disk(int fd, const char* path)  
{  
//獲取基本信息  
//... ...  
    //計(jì)算bmap  
    bmap_size = super_block.blocks_count/(8*HUST_BLOCKSIZE);  
    super_block.bmap_block = RESERVE_BLOCKS;  
  
    if (super_block.blocks_count%(8*HUST_BLOCKSIZE) != 0) {  
        bmap_size += 1;  
    }  
    bmap = (uint8_t *)malloc(bmap_size*HUST_BLOCKSIZE);  
    memset(bmap,0,bmap_size*HUST_BLOCKSIZE);  
  
    //計(jì)算imap  
    imap_size = super_block.inodes_count/(8*HUST_BLOCKSIZE);  
    super_block.imap_block = super_block.bmap_block + bmap_size;  
 
    if(super_block.inodes_count%(8*HUST_BLOCKSIZE) != 0) {  
        imap_size += 1;  
    }  
    imap = (uint8_t *)malloc(imap_size*HUST_BLOCKSIZE);  
    memset(imap,0,imap_size*HUST_BLOCKSIZE);  
  
    //計(jì)算inode_table  
    inode_table_size = super_block.inodes_count/(HUST_BLOCKSIZE/HUST_INODE_SIZE);  
    super_block.inode_table_block = super_block.imap_block + imap_size;  
    super_block.data_block_number = RESERVE_BLOCKS + bmap_size + imap_size + inode_table_size;  
    super_block.free_blocks = super_block.blocks_count - super_block.data_block_number - 1;  

    //設(shè)置bmap以及imap  
//... ...  
}

其中,imap和bmap為uint8_t的全局?jǐn)?shù)組虏肾。
計(jì)算完基本信息之后廓啊,我們需要將其寫入文件并創(chuàng)建根目錄和測(cè)試文件。文件創(chuàng)建的基本步驟如下:

  1. 檢測(cè)(獲确夂馈)磁盤(文件)大小谴轮,確認(rèn)是否有足夠的空間
  2. 找的空閑的inode和block,并標(biāo)記imap和bmap吹埠。
  3. 生成相應(yīng)的數(shù)據(jù)第步,并寫入對(duì)應(yīng)的塊中。對(duì)于根目錄來講缘琅,寫入的數(shù)據(jù)為三個(gè)目錄項(xiàng)粘都,目錄項(xiàng)的內(nèi)容為文件(目錄)名以及對(duì)應(yīng)的inode編號(hào)。第一個(gè)目錄項(xiàng)為當(dāng)前目錄和對(duì)應(yīng)的inode編號(hào)0刷袍,第二個(gè)目錄項(xiàng)為上一級(jí)目錄和對(duì)應(yīng)的inode編號(hào)0翩隧,第三個(gè)目錄項(xiàng)為歡迎文件,內(nèi)容為文件名“file”和對(duì)應(yīng)的inode編號(hào)1呻纹。
  4. 設(shè)置對(duì)應(yīng)的inode信息鸽心,如是文件還是目錄(mode信息),創(chuàng)建時(shí)間修改時(shí)間(i_ctime和i_mtime)居暖,用戶id和組id信息(i_uid和i_gid)等。
  5. 更新超級(jí)塊信息藤肢。

在我們的文件系統(tǒng)寫完之前太闺,我們可以新建一個(gè)文件來測(cè)試我們的mkfs是否能正常運(yùn)行,通過16進(jìn)制編輯器來查看是否功能正常嘁圈。具體步驟如下:

  1. 運(yùn)行下列命令創(chuàng)建文件省骂。
dd bs=4096 count=100 if=/dev/zero of=image 
  1. 編譯mkfs.c
 gcc mkfs.c -o mkfs 
  1. 格式化image文件
./mkfs ./image
  1. 通過hexdumo來查看文件的結(jié)構(gòu)蟀淮,結(jié)果如下圖。通過檢查钞澳,我們發(fā)現(xiàn)怠惶,image文件結(jié)構(gòu)寫入正確無誤。

文件系統(tǒng)的實(shí)現(xiàn)

一個(gè)通常意義上的文件系統(tǒng)驅(qū)動(dòng)可以單獨(dú)被編譯成模塊動(dòng)態(tài)加載轧粟,也可以被直接編譯到內(nèi)核中策治,為了調(diào)試的方便,本文中的文件系統(tǒng)采用動(dòng)態(tài)加載的方式實(shí)現(xiàn)兰吟。實(shí)現(xiàn)一個(gè)文件系統(tǒng)必須遵照內(nèi)核的一些“規(guī)則”通惫,以下我將以遞進(jìn)的順序闡述文件系統(tǒng)的實(shí)現(xiàn)過程。

一混蔼、 文件系統(tǒng)的加載與卸載

首先為了能夠成功加載文件系統(tǒng)履腋,文件系統(tǒng)需要提供文件系統(tǒng)的名字,超級(jí)塊的加載和刪除方法惭嚣。這些東西反應(yīng)在file_system,_type中遵湖。

struct file_system_type HUST_fs_type = {  
    .owner = THIS_MODULE,  
    .name = "HUST_fs",  
    .mount = HUST_fs_mount,  
    .kill_sb = HUST_fs_kill_superblock, /* unmount */  
};  

文件系統(tǒng)作為一種塊設(shè)備驅(qū)動(dòng),自然也需要實(shí)現(xiàn)module_init以及mocule_exit晚吞。代碼如下:

/* Called when the module is loaded. */  
int HUST_fs_init(void)  
{  
    int ret;  
    ret = register_filesystem(&HUST_fs_type);  
    if (ret == 0)  
        printk(KERN_INFO "Sucessfully registered HUST_fs\n");  
    else  
        printk(KERN_ERR "Failed to register HUST_fs. Error: [%d]\n", ret);  
    return ret;  
}  
  
/* Called when the module is unloaded. */  
void HUST_fs_exit(void)  

    int ret;  
    ret = unregister_filesystem(&HUST_fs_type);  
    if (ret == 0)  
        printk(KERN_INFO "Sucessfully unregistered HUST_fs\n");  
    else  
        printk(KERN_ERR "Failed to unregister HUST_fs. Error: [%d]\n", ret);  
}  
module_init(HUST_fs_init);  
module_exit(HUST_fs_exit);  
  
MODULE_LICENSE("MIT");  
MODULE_AUTHOR("cv");  

我們可以看到延旧,設(shè)備驅(qū)動(dòng)加載的時(shí)候,驅(qū)動(dòng)向內(nèi)核注冊(cè)了文件系統(tǒng)载矿,而驅(qū)動(dòng)卸載的時(shí)候垄潮,文件系統(tǒng)的信息也被刪除。文件系統(tǒng)加載時(shí)調(diào)用的函數(shù)為HUST_fs_mount闷盔,實(shí)際上弯洗,這個(gè)函數(shù)向內(nèi)核注冊(cè)了一個(gè)回調(diào):

int HUST_fs_fill_super(struct super_block *sb, void *data, int silent)  

這個(gè)函數(shù)是用來與VFS交互從而生成VFS超級(jí)塊的。在HUST fs中逢勾,超級(jí)塊在磁盤的第二個(gè)4096字節(jié)上牡整,即塊號(hào)為1。這個(gè)函數(shù)執(zhí)行時(shí)會(huì)從磁盤中讀取信息溺拱,填充到VFS提供的超級(jí)塊結(jié)構(gòu)體中逃贝,下列為部分關(guān)鍵代碼。

int HUST_fs_fill_super(struct super_block *sb, void *data, int silent)  {  
    struct buffer_head *bh;  
    bh = sb_bread(sb, 1);  
    struct HUST_fs_super_block *sb_disk;  
    sb_disk = (struct HUST_fs_super_block *)bh->b_data;  
    struct inode *root_inode;  
    if (sb_disk->block_size != 4096) {  
        printk(KERN_ERR "HUST_fs expects a blocksize of %d\n", 4096);  
        ret = -EFAULT;  
        goto release;  
   }  
   //fill vfs super block  
    sb->s_magic = sb_disk->magic;  
    sb->s_fs_info = sb_disk;  
    sb->s_maxbytes = HUST_BLOCKSIZE * HUST_N_BLOCKS; /* Max file size */  
    sb->s_op = &HUST_fs_super_ops;  
}  

從上述代碼可以看出迫摔,我們用sb_read來讀取磁盤上的內(nèi)容沐扳,然后填充super_block結(jié)構(gòu)體。值得注意的是句占,有關(guān)超級(jí)塊的操作函數(shù)即superblock_operations也是在此處賦值的沪摄。由于super_block* sb在文件系統(tǒng)卸載之前是一直存在于內(nèi)存中的,所以我們可以使用s_fs_info來存儲(chǔ)原始的超級(jí)塊信息,避免后期交互時(shí) 再次讀取磁盤杨拐。
文件系統(tǒng)卸載的時(shí)候超級(jí)塊信息需要被刪除祈餐,所以HUST_fs_kill_superblock的作用時(shí)釋放該超級(jí)塊,通知VFS該掛載點(diǎn)已經(jīng)卸載哄陶。
實(shí)現(xiàn)基本函數(shù)后帆阳,可以對(duì)文件系統(tǒng)進(jìn)行掛載操作,掛載操作的腳本內(nèi)容如下:

sudo umount ./test  
sudo rmmod HUST_fs  
dd bs=4096 count=100 if=/dev/zero of=image  
./mkfs image  
insmod HUST_fs.ko  
mount -o loop -t HUST_fs image ./test  
dmesg  

上述腳本屋吨,將項(xiàng)目下的test文件夾作為文件系統(tǒng)的掛載點(diǎn)蜒谤,并在掛載之后答應(yīng)出了內(nèi)核調(diào)試目錄。成功執(zhí)行該腳本的截圖如下:

我們可以看到test目錄已經(jīng)掛載成功而且內(nèi)核調(diào)試信息顯示文件系統(tǒng)掛載成功离赫。

二芭逝、 ls命令的實(shí)現(xiàn)

加載文件系統(tǒng)之后第一個(gè)要實(shí)現(xiàn)的功能是讀取文件系統(tǒng)中的數(shù)據(jù),所以選擇實(shí)現(xiàn)文件夾讀取操作渊胸,這一操作在2.x內(nèi)核中是.readdir函數(shù)指針旬盯,在最新版本中是,.iterate函數(shù)指針。這個(gè)指針在保存在file_operation中翎猛,如下所示胖翰。

const struct file_operations HUST_fs_dir_ops = {  
    .owner = THIS_MODULE,  
    .iterate = HUST_fs_iterate,  
}; 

HUST_fs_iterate函數(shù)主要功能邏輯是讀取inode的塊數(shù)據(jù),并且將塊數(shù)據(jù)中的inode和文件名通過dir_emit函數(shù)傳輸?shù)絍FS層切厘。以根目錄為例萨咳,根目錄的包含三個(gè)數(shù)據(jù)項(xiàng),分別是父目錄疫稿,當(dāng)前目錄和歡迎文件培他,所以該函數(shù)會(huì)執(zhí)行以下三個(gè)語句

//參數(shù)分別表示上下文,文件/目錄名遗座,文件/目錄名長度舀凛,inode號(hào),文件類型  
dir_emit(ctx, ".", 1,0, DT_DIR);  
dir_emit(ctx, "..", 2,0, DT_DIR);  
dir_emit(ctx, "file", 4,1, DT_REG);

完成該函數(shù)后途蒋,在填充根目錄inode時(shí)將HUST_fs_dir_ops指針賦值猛遍,即可在掛在文件系統(tǒng)后執(zhí)行l(wèi)s命令。

如上圖所示号坡,我們成功看到了歡迎文件懊烤。但是此時(shí)我們不能對(duì)文件進(jìn)行任何操作,因?yàn)檫€沒有實(shí)現(xiàn)其他的接口宽堆。

三腌紧、 磁盤管理相關(guān)邏輯的實(shí)現(xiàn)

這個(gè)磁盤管理的內(nèi)涵包括向磁盤寫入和從磁盤取出讀取inode,更新inode信息畜隶,維護(hù)imap寄啼,bmap逮光,inode table等操作。為了使磁盤上的內(nèi)容有序的組合起來墩划,磁盤空間的管理十分的重要,后續(xù)的文件讀寫操作都與此相關(guān)嗡综。
寫入和刪除inode的操作存放在super_operations這個(gè)結(jié)構(gòu)體中乙帮。

const struct super_operations HUST_fs_super_ops = {  
    .evict_inode = HUST_evict_inode,  
    .write_inode = HUST_write_inode,  
};

HUST_fs_super_ops需要在填充超級(jí)塊時(shí)賦值到super_block的s_ops字段中。HUST_write_inode函數(shù)的功能是將內(nèi)存中的inode保存在磁盤上极景。關(guān)鍵代碼如下察净。

int HUST_write_inode(struct inode *inode, struct writeback_control *wbc)  
{  
    struct buffer_head * bh;  
    struct HUST_inode * raw_inode = NULL;  
    HUST_fs_get_inode(inode->i_sb, inode->i_ino, raw_inode);  
    if (!raw_inode)  
        return -EFAULT;  
    raw_inode->mode = inode->i_mode;  
    raw_inode->i_uid = fs_high2lowuid(i_uid_read(inode));  
    raw_inode->i_gid = fs_high2lowgid(i_gid_read(inode));  
    raw_inode->i_nlink = inode->i_nlink;  
    raw_inode->file_size = inode->i_size;      
    raw_inode->i_atime = (inode->i_atime.tv_sec);  
    raw_inode->i_mtime = (inode->i_mtime.tv_sec);  
    raw_inode->i_ctime = (inode->i_ctime.tv_sec);  
    mark_buffer_dirty(bh);  
    brelse(bh);  
    return 0;  
}  

可以看到,該函數(shù)的將vfs inode中的相關(guān)信息存儲(chǔ)到HUST_inode結(jié)構(gòu)體中盼樟,然后寫入磁盤氢卡。這個(gè)是單獨(dú)的寫入磁盤操作,事實(shí)上晨缴,當(dāng)我們申請(qǐng)inode時(shí)译秦,imap也是需要檢查刷新的,需要把相應(yīng)位置標(biāo)記為1击碗。同理筑悴,evict_inode函數(shù)的作用時(shí)刪除inode,刪除成功后稍途,我們需要刷新imap的值阁吝,把相應(yīng)位置標(biāo)記為0。
設(shè)置和寫入map的操作都在map.c中械拍,以下以imap為例突勇。對(duì)于imap來講,申請(qǐng)inode的時(shí)候需要檢查第一個(gè)空閑的inode編號(hào)坷虑,當(dāng)inode被釋放的時(shí)候也要及時(shí)清零對(duì)應(yīng)的imap甲馋。與此相關(guān)的函數(shù)如下。

//從磁盤中讀取數(shù)據(jù)并存在imap數(shù)組中  
int get_imap(struct super_block* sb, uint8_t* imap, ssize_t imap_size);  
//在vaddr數(shù)組中找到第一個(gè)為0的bit猖吴,這個(gè)函數(shù)用于定位空inode或者block  
int HUST_find_first_zero_bit(const void *vaddr, unsigned size);  
//將imap的某一位置0或者1摔刁,并保存在磁盤上  
int set_and_save_imap(struct super_block* sb, uint64_t inode_num, uint8_t value);  
//定義的位操作宏如下  
#define setbit(number,x) number |= 1UL << x  
#define clearbit(number, x) number &= ~(1UL << x)  

由于本文件系統(tǒng)并不是為了實(shí)際使用,所以上述的操作都沒有考慮性能以及準(zhǔn)確性問題海蔽。事實(shí)上共屈,能夠加上校驗(yàn)或者冗余備份是最好的。

四党窜、讀寫文件內(nèi)容

為了能夠快速看到文件系統(tǒng)在正常工作拗引,所以接下來需要實(shí)現(xiàn)文件的讀寫操作。文件讀寫操作按照一般處理幌衣,應(yīng)該是實(shí)現(xiàn)在struct file_operations這個(gè)結(jié)構(gòu)體中的矾削。事實(shí)上壤玫,最開始我是實(shí)現(xiàn)在這個(gè)結(jié)構(gòu)體中的read_iter函數(shù)指針中的。但是比較有趣的一點(diǎn)是哼凯,如果我們實(shí)現(xiàn)了struct address_space_operations結(jié)構(gòu)體中的函數(shù)欲间,那么struct file_operations結(jié)構(gòu)體中的函數(shù)則可以交由VFS實(shí)現(xiàn)。代碼如下:

const struct file_operations HUST_fs_file_ops = {  
    .owner = THIS_MODULE,  
    .llseek = generic_file_llseek,  
    .mmap = generic_file_mmap,  
    .fsync = generic_file_fsync,  
    .read_iter = generic_file_read_iter,  
    .write_iter = generic_file_write_iter,  
};  
const struct   address_space_operations HUST_fs_aops = {  
    .readpage = HUST_fs_readpage,  
    .writepage = HUST_fs_writepage,  
    .write_begin = HUST_fs_write_begin,  
    .write_end = generic_write_end,  
};

上述的generic開頭的函數(shù)是不需要我們手動(dòng)實(shí)現(xiàn)的断部。上述的address_space_operations操作其實(shí)是實(shí)現(xiàn)了頁高速緩存的一些操作猎贴。頁高速緩存是linux內(nèi)核實(shí)現(xiàn)的一種主要磁盤緩存,它主要用來減少對(duì)磁盤的IO操作蝴光,具體地講她渴,是通過把磁盤中的數(shù)據(jù)緩存到物理內(nèi)存中,把對(duì)磁盤的訪問變?yōu)閷?duì)物理內(nèi)存的訪問蔑祟。這些接口一旦實(shí)現(xiàn)趁耗,那么對(duì)文件的操作就可以轉(zhuǎn)移到內(nèi)存中,這就是為什么可以使用generic開頭的這些函數(shù)來代替手寫疆虚。
HUST_fs_readpage, HUST_fs_writepage以及HUST_fs_write_begin都被注冊(cè)回調(diào)到同一個(gè)函數(shù)HUST_fs_get_block苛败。HUST_fs_get_block主要返回內(nèi)核請(qǐng)求長度的數(shù)據(jù)。至于讀寫操作装蓬,內(nèi)核調(diào)用__bwrite函數(shù)最終調(diào)用塊設(shè)備驅(qū)動(dòng)執(zhí)行著拭。因?yàn)樵谖覜]有采用二級(jí)或者多級(jí)索引,故而HUST_fs_get_block函數(shù)邏輯比較簡單牍帚,部分代碼如下:

int HUST_fs_get_block(struct inode *inode, sector_t block,  
              struct buffer_head *bh, int create)  
{  
    struct super_block *sb = inode->i_sb;  
    if (block > HUST_N_BLOCKS) 
        return -ENOSPC;  
    struct HUST_inode H_inode;  
    if (-1 == HUST_fs_get_inode(sb, inode->i_ino, &H_inode))  
        return -EFAULT;  
    if (H_inode.blocks == 0)  
        if(alloc_block_for_inode(sb, &H_inode, 1)) 
            return -EFAULT;  
    map_bh(bh, sb, H_inode.block[block]);  
    return 0;  
}

如上所示儡遮,該函數(shù)判斷傳入的block的大小,并將磁盤內(nèi)容映射到bh中暗赶。后續(xù)的讀寫操作將有VFS幫我們完成鄙币。

五、inode操作

Inode操作涉及文件(夾)的創(chuàng)建刪除蹂随,將HUST_inode映射到VFS中的inode等操作十嘿。具體實(shí)現(xiàn)的函數(shù)如下。

const struct inode_operations HUST_fs_inode_ops = {  
    .lookup = HUST_fs_lookup,  
    .mkdir = HUST_fs_mkdir,  
    .create = HUST_fs_create,  
    .unlink = HUST_fs_unlink,  
}; 

HUST_fs_lookup是其中比較復(fù)雜的一個(gè)函數(shù)岳锁,它負(fù)責(zé)將一個(gè)目錄下的inode信息交由VFS管理绩衷。首先,HUST_fs_lookup讀取文件夾的內(nèi)容激率,然后遍歷文件夾下面的HUST_inode咳燕,找到我們想要的HUST_inode,根據(jù)不同的文件屬性乒躺,申請(qǐng)vfs_inode招盲;并對(duì)不同的vfs_inode設(shè)置不同的操作。假設(shè)vfs_inode對(duì)應(yīng)的是一個(gè)文件嘉冒,那么就設(shè)置vfs_inode->mapping->a_ops曹货,如果vfs_inode對(duì)應(yīng)的是文件夾咆繁,那么就設(shè)置vfs_inode->f_ops = &HUST_fs_dir_ops;最后將vfs_inode注冊(cè)到VFS中。這部分的關(guān)鍵代碼如下:

struct dentry *HUST_fs_lookup(struct inode *parent_inode,  
                  struct dentry *child_dentry, unsigned int flags)  
{  
    struct super_block *sb = parent_inode->i_sb;  
    struct HUST_inode H_inode;  
//省略代碼  
    for (i = 0; i < H_inode.dir_children_count; i++) {  
        if (strncmp  
            (child_dentry->d_name.name, dtptr[i].filename,  
             HUST_FILENAME_MAX_LEN) == 0){  
            inode = iget_locked(sb, dtptr[i].inode_no);  
            if (inode->i_state & I_NEW) {  
                inode_init_owner(inode, parent_inode, 0);  
                struct HUST_inode H_child_inode;  
                if (-1 == HUST_fs_get_inode(sb, dtptr[i].inode_no, &H_child_inode))  
                    return ERR_PTR(-EFAULT);  
                HUST_fs_convert_inode(&H_child_inode, inode);  
                inode->i_op = &HUST_fs_inode_ops;  
                if (S_ISDIR(H_child_inode.mode)) {  
                    inode->i_fop = &HUST_fs_dir_ops;  
                } else if (S_ISREG(H_child_inode.mode)) {  
                    inode->i_fop = &HUST_fs_file_ops;;  
                    inode->i_mapping->a_ops = &HUST_fs_aops;  
                }  
                inode->i_mode = H_child_inode.mode;  
                inode->i_size = H_child_inode.file_size;  
                insert_inode_hash(inode);  
                unlock_new_inode(inode);  
            }  
        }  
    }  
//省略代碼  
} 

只有在這里注冊(cè)了相關(guān)函數(shù)顶籽,系統(tǒng)調(diào)用才能正常執(zhí)行玩般。不然就會(huì)出現(xiàn)不支持的操作這種報(bào)錯(cuò)信息。
.create與.mkdir都是對(duì)應(yīng)了inode的創(chuàng)建礼饱,只是inode的屬性不能而已壤短。.create創(chuàng)建普通文件而.mkdir創(chuàng)建文件夾。所以這兩個(gè)函數(shù)的功能被函數(shù)HUST_fs_create_obj所處理慨仿。這個(gè)函數(shù)接受新建文件(夾)的請(qǐng)求,檢查磁盤的大小纳胧,檢查是否有空余的indoe镰吆,并且分配inode號(hào),然后更新imap信息跑慕,最后更新超級(jí)塊信息万皿。由于該函數(shù)邏輯簡單但是代碼量比較大,故而不在此展示其具體實(shí)現(xiàn)核行。
在完成上述工作之后牢硅,我們的文件系統(tǒng)基本已經(jīng)完成了,這個(gè)系統(tǒng)采用線性(區(qū)別于minixi二級(jí)索引用樹來管理)的方式管理磁盤空間芝雪,支持基本的增刪改查文件操作减余,支持文件權(quán)限,支持多用戶惩系。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末位岔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子堡牡,更是在濱河造成了極大的恐慌抒抬,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晤柄,死亡現(xiàn)場(chǎng)離奇詭異擦剑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)芥颈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門惠勒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人浇借,你說我怎么就攤上這事捉撮。” “怎么了妇垢?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵巾遭,是天一觀的道長肉康。 經(jīng)常有香客問我,道長灼舍,這世上最難降的妖魔是什么吼和? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮骑素,結(jié)果婚禮上炫乓,老公的妹妹穿的比我還像新娘。我一直安慰自己献丑,他們只是感情好末捣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著创橄,像睡著了一般箩做。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妥畏,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天邦邦,我揣著相機(jī)與錄音,去河邊找鬼醉蚁。 笑死燃辖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的网棍。 我是一名探鬼主播黔龟,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼确沸!你這毒婦竟也來了捌锭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤罗捎,失蹤者是張志新(化名)和其女友劉穎观谦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桨菜,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡豁状,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了倒得。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泻红。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖霞掺,靈堂內(nèi)的尸體忽然破棺而出谊路,到底是詐尸還是另有隱情,我是刑警寧澤菩彬,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布缠劝,位于F島的核電站潮梯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏惨恭。R本人自食惡果不足惜秉馏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脱羡。 院中可真熱鬧萝究,春花似錦、人聲如沸锉罐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锯七。三九已至嗡贺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抖拦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工舷暮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留态罪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓下面,卻偏偏與公主長得像复颈,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沥割,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容