F2FS源码分析-3.1 [F2FS 文件创建和删除部分] 一般文件的创建
F2FS的文件創建流程
文件創建流程介紹
linux的文件的創建可以抽象為兩個流程
到具體代碼,上述兩個抽象流程在F2FS中主要包含了以下幾個子流程:
第一步的vfs_open函數是VFS層面的流程,下面僅針對涉及F2FS的文件創建流程,且經過簡化的主要流程進行分析。
前置概念: inode和f2fs_inode_info
眾所周知,inode結構是linux的vfs層最核心的結構之一,反應了文件的應該具有的基礎信息,但是對于一些文件系統,原生的inode結構的信息并不夠,還需要增加一些額外的變量去支持文件系統的某些功能,同時為了保證vfs層對所有文件系統的兼容性,我們直接修改inode結構不是一個明智的方法。針對這種場景,f2fs使用了一種叫f2fs_inode_info的結構去擴展原有的inode的功能。
相互轉換
從inode到f2fs_inode_info:
static inline struct f2fs_inode_info *F2FS_I(struct inode *inode) {return container_of(inode, struct f2fs_inode_info, vfs_inode); }從f2fs_inode_info到inode:
// vfs的inode其實是f2fs_inode_info結構體的一個內部變量 struct f2fs_inode_info {struct inode vfs_inode; /* serve a vfs inode */... };// 因此訪問可以直接指向 struct f2fs_inode_info *fi = F2FS_I(inode); fi->vfs_inode // 這里 fi->vfs_inode == inode從上面代碼我們可以看出,f2fs中的inode是f2fs_inode_info當中的一個內部變量,因此可以用container_of這個函數直接獲得,也可以通過指針獲得。
F2FS中的VFS inode的創建和銷毀
我們一般使用VFS提供的new_inode函數創建一個新inode。這個new_inode函數內部會調用new_inode_pseudo函數,然后再調用alloc_inode函數,最后調用f2fs_alloc_inode函數,我們從這里開始分析:
如下代碼,顯然就是通過內存分配函數先創建一個f2fs_inode_info然后返回給上層:
static struct inode *f2fs_alloc_inode(struct super_block *sb) {struct f2fs_inode_info *fi;fi = kmem_cache_alloc(f2fs_inode_cachep, GFP_F2FS_ZERO); //簡單直接創建f2fs_inode_infoif (!fi)return NULL;init_once((void *) fi); // 這個函數初始化vfs inode部分的原始信息// 下面開始初始化f2fs_inode_info部分的原始信息atomic_set(&fi->dirty_pages, 0);init_rwsem(&fi->i_sem);...return &fi->vfs_inode; // 返回的vfs_inode給上層 }當vfs inode的link是0的時候,它應當被銷毀。由于vfs inode是f2fs_inode_info的內部變量,它如何被銷毀呢:
// 用戶傳入一個inode銷毀 static void f2fs_destroy_inode(struct inode *inode) {call_rcu(&inode->i_rcu, f2fs_i_callback); }同樣簡單直接,free掉這塊內存就行
static void f2fs_i_callback(struct rcu_head *head) {struct inode *inode = container_of(head, struct inode, i_rcu);kmem_cache_free(f2fs_inode_cachep, F2FS_I(inode)); }f2fs_create函數
這個函數的主要作用是創建vfs_inode,并鏈接到對應的目錄下,核心流程就是先創建該文件的基于f2fs的inode結構(參考xxx),以及它對應的f2fs的inode page,即f2fs_inode。然后設置函數指針,最后將這個f2fs的inode page鏈接到對應的目錄下。
static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,bool excl) {struct f2fs_sb_info *sbi = F2FS_I_SB(dir);struct inode *inode;nid_t ino = 0;int err;inode = f2fs_new_inode(dir, mode); // 創建f2fs特定的inode結構inode->i_op = &f2fs_file_inode_operations; // 然后賦值對應的函數指針inode->i_fop = &f2fs_file_operations;inode->i_mapping->a_ops = &f2fs_dblock_aops;ino = inode->i_ino; // 記錄該inode的inoerr = f2fs_add_link(dentry, inode); // 將該inode鏈接到用戶傳入的父目錄dir中if (err)goto out;f2fs_alloc_nid_done(sbi, ino); // 在f2fs_new_inode函數內分配了ino,在這里完成最后一步return 0; }f2fs_new_inode函數
下面繼續分析f2fs_new_inode函數(只顯示主干部分),這個函數創建inode結構,還沒創建對應的f2fs inode page
static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) {struct f2fs_sb_info *sbi = F2FS_I_SB(dir);nid_t ino;struct inode *inode;bool nid_free = false;int xattr_size = 0;int err;inode = new_inode(dir->i_sb); // 先創建出來一個沒有ino的inode結構,參考前面提及的創建流程if (!f2fs_alloc_nid(sbi, &ino)) { // 然后給這個inode分配一個nid,即inogoto fail;}nid_free = true;inode_init_owner(inode, dir, mode); // 初始化從屬信息: 訪問模式、父目錄等inode->i_ino = ino; // 初始化一些元數據信息,例如inoinode->i_blocks = 0;inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);F2FS_I(inode)->i_crtime = inode->i_mtime;inode->i_generation = sbi->s_next_generation++;err = insert_inode_locked(inode); // 將這個inode插入到全局的inode table(VFS行為)set_inode_flag(inode, FI_NEW_INODE); // 注意這個標志位后面會用到......// 上面省略代碼都在設置法f2fs_inode_info的flag,并在這個函數將部分flag設置到vfs inode中f2fs_set_inode_flags(inode); return inode; }f2fs_add_link函數
經過上面的函數,我們已經創建了一個f2fs使用的vfs inode,接下來我們要將這個inode鏈接到父目錄的inode當中,建立聯系,f2fs_add_link函數直接會調用f2fs_do_add_link函數,因此我們直接分析這個函數。其中f2fs_dir_entry代表是目錄項,具體的作用含義在目錄項的作用相關章節(新坑待填)介紹,這里可以理解為父目錄包含了多個子文件/目錄項,每一個目錄項對應一個子文件/子目錄的關聯信息。我們將上一節新創建的inode加入到父目錄的管理,也就是在父目錄中為這個新inode下創建一個目錄項。
static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) {// 這里的dentry就是新inode的dentryreturn f2fs_do_add_link(d_inode(dentry->d_parent), &dentry->d_name,inode, inode->i_ino, inode->i_mode); }// dir是父目錄 int f2fs_do_add_link(struct inode *dir, const struct qstr *name,struct inode *inode, nid_t ino, umode_t mode) {struct f2fs_dir_entry *de = NULL; // 父目錄dir的目錄項,初始化為NULLint err;// 如果文件已經加密,則獲得解密后的名字fnameerr = fscrypt_setup_filename(dir, name, 0, &fname); if (de) { // 如果找到目錄項f2fs_put_page(page, 0);err = -EEXIST;} else if (IS_ERR(page)) {err = PTR_ERR(page);} else { // 對于一個新inode,它對應的父目錄的目錄項f2fs_dir_entry應該是不存在的err = f2fs_add_dentry(dir, &fname, inode, ino, mode);}return err; }這個f2fs_add_dentry函數提取了文件名字的字符串以及字符串長度:
int f2fs_add_dentry(struct inode *dir, struct fscrypt_name *fname,struct inode *inode, nid_t ino, umode_t mode) {struct qstr new_name;int err = -EAGAIN;new_name.name = fname_name(fname); // 將文件名的字符串格式保存在這里new_name.len = fname_len(fname); // 將文件名的長度保存在這里// 在這個函數實現新inode和父inode的鏈接err = f2fs_add_regular_entry(dir, &new_name, fname->usr_fname,inode, ino, mode);f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); // 更新修改時間return err; }新inode的f2fs_dir_entry應該是不存在的,注意我們f2fs_new_inode函數一節提到的FI_NEW_INODE的flag。
int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name,const struct qstr *orig_name,struct inode *inode, nid_t ino, umode_t mode) {...// 上面的機制比較復雜,在這里不提,在目錄項的作用相關章節再提// 上面做了一大堆事情可以理解為,根據[文件名的長度]創建一個新的f2fs_dir_entry,然后加入到父目錄當中// 需要注意的是這個f2fs_dir_entry還沒有包含新inode的信息// 接下來就是要做的就是// 1. 為新的vfs inode創建inode page,初始化與父目錄有關的信息// 2. 基于新inode的信息(名字,ino等)更新f2fs_dir_entryif (inode) {// 這個函數就是創建inode page,初始化與父目錄有關的信息page = f2fs_init_inode_metadata(inode, dir, new_name,orig_name, NULL);}// 基于新inode的信息(名字,ino等)更新f2fs_dir_entryf2fs_update_dentry(ino, mode, &d, new_name, dentry_hash, bit_pos);set_page_dirty(dentry_page);f2fs_update_parent_metadata(dir, inode, current_depth); // 清除FI_NEW_INODE的flagreturn err; }由于新inode設置了FI_NEW_INODE,因此f2fs_init_inode_metadata函數就是完成了兩個功能:
將新的inode鏈接到父目錄后,后續用戶訪問時,可以通過父目錄找到新創建的文件的inode,即完成了整個文件的創建流程。
總結
以上是生活随笔為你收集整理的F2FS源码分析-3.1 [F2FS 文件创建和删除部分] 一般文件的创建的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu下F2FS文件系统的安装与挂
- 下一篇: 帆软报表使用心得(转)