日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

F2FS源码分析-3.1 [F2FS 文件创建和删除部分] 一般文件的创建

發布時間:2023/12/31 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 F2FS源码分析-3.1 [F2FS 文件创建和删除部分] 一般文件的创建 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

F2FS的文件創建流程

文件創建流程介紹

linux的文件的創建可以抽象為兩個流程

  • 創建一個inode,使得包含文件的元數據信息;
  • 將這個新創建的inode加入父目錄的管理當中,可以理解建立父目錄與這個新inode的關系。
  • 到具體代碼,上述兩個抽象流程在F2FS中主要包含了以下幾個子流程:

  • 調用vfs_open函數
  • 調用f2fs_create函數: 創建文件inode,并連接到父目錄
  • f2fs_new_inode函數創建inode
  • f2fs_add_link函數鏈接到父目錄
  • 第一步的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 page,然后初始化acl、security等信息。
  • 然后初始化新創建的inode page的名字
  • 再增加inode的引入鏈接。
  • struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir,const struct qstr *new_name, const struct qstr *orig_name,struct page *dpage) {struct page *page;int err;// 由于新inode設置了FI_NEW_INODEif (is_inode_flag_set(inode, FI_NEW_INODE)) {// 創建一個新的inode page,然后初始化acl、security等信息。page = f2fs_new_inode_page(inode);err = f2fs_init_acl(inode, dir, page, dpage);if (err)goto put_error;err = f2fs_init_security(inode, dir, orig_name, page);if (err)goto put_error;}} else {page = f2fs_get_node_page(F2FS_I_SB(dir), inode->i_ino);if (IS_ERR(page))return page;}if (new_name) { // 然后初始化新創建的inode page的名字init_dent_inode(new_name, page);if (f2fs_encrypted_inode(dir))file_set_enc_name(inode);}// 再增加inode的引入鏈接。if (is_inode_flag_set(inode, FI_INC_LINK))f2fs_i_links_write(inode, true);return page; }

    將新的inode鏈接到父目錄后,后續用戶訪問時,可以通過父目錄找到新創建的文件的inode,即完成了整個文件的創建流程。

    總結

    以上是生活随笔為你收集整理的F2FS源码分析-3.1 [F2FS 文件创建和删除部分] 一般文件的创建的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。