




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、FUSE源碼剖析1.刖百本文是對FUSE-2.9.2源碼的學習總結。FUSE代碼在用戶空間和內核空間都有運行,為了突出重點,先簡要描述了在基于FUSE的用戶空間文件系統中執行write操作的一般流程,接下來介紹了 重要的數據結 構,最后以FUSE的運行過程為線索,剖析 FUSE程序運行過程的 3 個關鍵步驟:1 .FUSE模塊加載2 .mount 和 open 過程3 .對文件write。對于虛擬文件系統和設備驅動的相關概念本文僅作簡要說明。需要說明的是,由于內核的 復雜性及個人能力的有限,本文省略了包括內核同步,異常檢查在內的諸多內容,希望可以突 出重點。2. FUSE下write的一般流程
2、recvead在基于FUSE的用戶空間文件系統中執行中部分函數是縮寫,請參考源碼):圖iwrite操作的流程如圖1所示(由于版面關系,圖1 .客戶端在 mount目錄下面,對一個 regular file調用write,這一步是在用戶空間執行2 .write內部會調用虛擬文件系統提供的一致性接口vfs_write3 .根據 FUSE 模塊注冊的 file_operations 信息,vfs_write 會調用 fuse_file_aio_write ,將寫請 求放入fuse connection的request pending queue,隨后進入睡眠等待應用程序reply4 .用戶空間的li
3、bfuse有一個守護進程通過函數fuse_session_loop輪詢雜項設備/dev/fuse, 一旦 request queue 有請求即通過 fuse_kern_chan_receive 接收5 .fuse_kern_chan_receive通過read讀取request queue中的內容,read系統調用實際上是調 用的設備驅動接口 fuse_dev_read6 .在用戶空間讀取并分析數據,執行用戶定義的write操作,將狀態通過 fuse_reply_write返回給kernel7 .fuse_reply_write 調用 VFS 提供的一致性接口vfs_write8 .vfs_w
4、rite最終調用fuse_dev_write將執行結果返回給第3步中等待在 waitq的進程,此進程得到reply后,write返回3. 數據結構本節主要介紹了 FUSE中比較重要的數據結構,需要說明的是圖示中只列出了與敘述相關的數據成員,完整的數據結構細節請參考源碼。3.1. 內核部分file ypcmliuwstnicr fttw conn男IWljiwid mwct idffudlImui yiuup_|irm Ihu 上Rki UTilOruc- r上 UH± iilhuJfa I nia|5turl head: pe-ih 生*lllk- liM 同Hid H.«
5、drwl )ifcr-ilDH' wu由3l 2|ib bkcL *abvtrikrt hk- _-1 * 一 Tuara -s* - 55rnndlliftK w%crwt itiZe73 u-q MpenhiEns-i叩多zi wl二 niLrde y iMdsiruct list hejdf <"P Linksiiiua ii4i mf :W llinkstruct fuse_conn :每一次 mount 會實例化一個GT2 super lilni_k.,工出>irud :IIIKW I 日lTI工LKmfCL, 19fiibszine,JumI 4
6、9;ihliustruct fuse_conn 即 fuse connection,它代表了用戶空間和內核的通信連接。fuse connection維護了包括 pending list, processing list和io list在內的request queue, fuse connection通過這些隊列管理用戶空間和內核空間通信過程。struct fuse_req :每次執行系統調用時會生成一個struct fuse_req,這些fuse_req依據state被組織在不同的隊列中,struct fuse_conn維護了這些隊列.struct file:存放打開文件與進程之間進行交互的有
7、關信息,描述了進程怎樣與一個打開的文件進行交互,這類信息僅當進程訪問文件期間存在于內核內存中。struct inode:文件系統處理文件所需要得所有信息都放在一個名為inode(索引節點)的數據結構中。文件名可以隨時更改,但是索引節點對文件是唯一的,并且隨著文件的存在而存在。struct file operation :定義了可以對文件執行的操作。3.2. 用戶空間部分Llukit6J 1 uniqueftrtaqt 加河 qh 岸nh導trptphiin ,耳h. in jet fifrrf Lh:mstruc-t iti?rf_m| *ncximtlki tuMcrwi *nrxt4nld
8、: fifcrf_re| next>lnici (W rcq 多preiilnrl lUK m| *pirv$ln>cL l& rvc| *prciMLr uulL hi9E必Uli他“void *darrawbilile inE rxilcdEm 仲找 chnn FhULru(64_( uniqueui ni64_t iiiiiqueEiwci fltsesi% aid (|prw«s_hul)( th明 buC dumel)叫 *rwrivr bidl n、bu£iTfoec fbsJI *rstruct fuse_req :這個結構和上文中內核的f
9、use_req同名,有著類似的作用,但是數據成員不同。struct fuse_session :定義了客戶端管理會話的結構體,包含了一組對session可以執行的操作。struct fuse_chan :定義了客戶端與FUSE內核連接通道的結構體,包含了一組對channel可以執行的操作。struct fuse_ll_ops :結構的成員為一個函數指針func和命令名字符串 name,內核中發過來的每一個request最后都映射到以此結構為元素的數組中。4. FUSE模塊力口載FUSE內核模塊需要在用戶空間使用insmod或者 modprobe加載。它們通過系統調用init_module啟動加
10、載過程,注冊過程比較簡單,包括如下步驟:1 .創建高速緩存結構fuse_inode_cachep2 .遍歷file_systems鏈表,如果未注冊,則將 fuseblk_fs_type鏈到file_systems鏈表尾部3 .遍歷file_systems鏈表,如果未注冊,則將 fuse_fs_type鏈到file_systems鏈表尾部4 .創建 fuse_kobj 和 connections_kobj 兩個 kobject5 .遍歷file_systems鏈表,如果未注冊,則將 fuse_ctl_fs_type鏈到file_systems鏈表尾部 模塊成功加載以后,以下接口被注冊1 stat
11、ic struct file_system_type fuseblk_fs_type = / 塊設備2 .owner= THIS_MODULE,3 .name= "fuseblk",4 .mount= fuse_mount_blk,5 .kill_sb= fuse_kill_sb_blk,6 .fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE, 7 ;89 static struct file_system_type fuse_fs_type = 10 .owner= THIS_MODULE,11 .name= "fuse&q
12、uot;,12 .fs_flags = FS_HAS_SUBTYPE,13 .mount= fuse_mount,14 .kill_sb = fuse_kill_sb_anon,15 ;1617 const struct file_operations fuse_dev_operations = 18 .owner = THIS_MODULE,19 .llseek= no_llseek,20 .read= do_sync_read,21 .aio_read = fuse_dev_read,22 .splice_read = fuse_dev_splice_read,23 .write = do
13、_sync_write,24 .aio_write = fuse_dev_write,25 .splice_write = fuse_dev_splice_write,26 .poll = fuse_dev_poll,27 .release= fuse_dev_release,28 .fasync= fuse_dev_fasync, 29 ;3031 static struct miscdevice fuse_miscdevice = 32 .minor = FUSE_MINOR,33 .name = "fuse",34 .fops = &fuse_dev_oper
14、ations,35 ;5. mount 和 open 過程FUSE模塊加載注冊了 fuseblk_fs_type和fuse_fs_type兩種文件類型,默認情況下使用的是 fuse_fs_type 即 mount 函數指針被初始化為fuse_mount, 而 fuse_mount 實際調用 mount_nodev ,它主要由如下兩步組成:1.sget(fs_type)搜索文件系統的超級塊對象(super_block)鏈表(type->fs_supers),如果找到一個與塊設備相關的超級塊,則返回它的地址。否則,分配并初始化一個新的超級塊對象,把它插 入到文件系統鏈表和超級塊全局鏈表中,并
15、返回其地址。2.fill_super(此函數由各文件系統自行定義):這個函數式各文件系統自行定義的函數,它實際 上是fuse_fill_super。一般fill_super會分配索引節點對象和對應的目錄項對象,并填充超級塊字段值,另外對于fuse還需要分配fuse_conn,fuse_req。需要說明的是,它在底層調用了 fuse_init_file_inode 用 fuse_file_operations 和 fuse_file_aops 分另I初始化 inode->i_fop 和 inode->i_data.a_ops。1 static const struct file_op
16、erations fuse_file_operations = 2.llseek=fuse_file_llseek,3.read=do_sync_read,4.aio_read=fuse_file_aio_read,5.write=do_sync_write,6.aio_write=fuse_file_aio_write,7.mmap=fuse_file_mmap,8.open=fuse_open,9.flush=fuse_flush,10.release=fuse_release,11.fsync=fuse_fsync,12.lock=fuse_file_lock,13.flock=fuse
17、_file_flock,14.splice_read = generic_file_splice15.unlocked_joctl = fuse_file_ioctl,16.compat_ioctl = fuse_file_compat17.poll=fuse_file_poll,18.fallocate=fuse_file_fallocate,19 ;2021 static const struct address_space_operations fuse_file_aops = 22 .readpage = fuse_readpage,23 .writepage = fuse_write
18、page,24 .launder_page = fuse_launder_page,25 .readpages= fuse_readpages,26 .set_page_dirty = _set_page_dirty_nobuffers,27 .bmap = fuse_bmap,28 .direct。=fuse_direct_IO,29 ;open系統調用底層實現相當復雜,它的主要工作是實例化file對象。file->f_op就是在open中被賦值為inode->i_fop ,這一過程讀者可以在fs/open.c中的do_entry_open函數中找到。如上所述,inode->
19、;i_fop 已經被 fuse_init_file_inode 初始化為 fuse_file_operations。至此,普通文件和設備文件的操作接口都已成功初始化。6. FUSE用戶空間流程FUSE 在用戶空間提供了fuse userspace library 和 mount /unmount 。 fuse usespace library 提供了一組API供用戶開發用戶空間文件系統。用戶要做的就是實現fuse_operations或fuse_lowlevel_ops定義的操作,這兩個結構類似于VFS中的struct file_operations。mount工具fusermount用于掛載
20、用fuse實現的文件系統。用戶在使用fuse的時候有兩種開發模式:一種是 high-level模式,此I式下fuse的入口函數為fuse_main ,它封裝了一系列初始化操作,使用簡單,但是不靈活。另一種是low-level模式,用戶可以利用fuse提供的底層函數靈活開發應用程序。需要說明的是 high-level模式其實是對low-level的封裝,因此這里分析lowlevel模式。圖4圖4展示FUSE在用戶空間總體工作流程:1 .調用fuse_mount實例化struct fuse_chan為ch,將指定目錄 mount到掛載點2 .實例化 struct fuse_session為se,并
21、且將 se和ch關聯3 .進入循環,從/dev/fuse讀取數據,處理以后執行響應的操作圖5圖5展示了 fuse_mount函數內部流程:4 .確保打開的文件描述符至少大于25 .分析并檢查用戶傳入的參數6 .打開/dev/fuse得到fd ,用戶空間與內核通過/dev/fuse通信7 . mount源目錄到掛載點8 .用 fd 實例化 struct fuse_chan 為 ch9 .返回ch圖6圖 6 展示了 fuse_mount_compat25 內部細節,進入循環以后,函數 fuse_session_receive_buf 實際通過fuse_ll_receive_buf從/dev/fus
22、e中讀取數據,其通過 fbuf返回。fuse_ll_receive_buf是通過read或者splice系統調用從內核request隊列中讀取數據。函數fuse_session_process_buf 實際通過 fuse_ll_process_buf 處理數據,fuse_ll_process_buf 會根據數據fuse提供了一組類似 writev將結果傳入內核。類型最后執行用戶定義的操作fuse_ll_opsin->opcode.func(req, in->nodeid, inarg)執行完用戶定義的操作以后需要向內核返回執行結果,fuse_reply_XXX 的API,這些API
23、最后實際通過系統調用7. FUSE內核部分流程FUSE在內核空間執行的部分主要包括FUSE模塊加載以及雜項設備驅動。模塊加載過程已經在第4節介紹,這一節主要描述從request隊列讀寫請求的流程。FUSE設備驅動程序本質上是一個生產者消費者模型。生產者為用戶在掛載目錄下對普request然后將去放入通文件(regular file)執行的系統調用,每一次系統調用會產生一個 pending list。pending list能存放的元素個數只和系統內存有關;消費者為用戶對設備文件/dev/fuse 或者/dev/fuseblk 的 read,這操作會去 pending list 或 interr
24、upt list 取 request,當 list 為空時,進程主動 schedule讓出CPU。request結構的細節在第 3節已經介紹,此處不贅述。 enmu fuse_req_state定義了 request的6 種狀態,其含義分別為:FUSE_REQ_INIT :請求被初始化FUSE_REQ_PENDING :請求掛起待處理FUSE_REQ_READING :請求正在讀FUSE_REQ_SENT :請求被發送FUSE_REQ_WRITING :請求正在寫FUSE REQ FINISHED :請求已經完成_ I訪總fiEejtritoci 魚羊附帖 酬_ 和瞪_8 僮ifuMjetNq
25、hk式久Wjwi ®_pa 解 i圖7是在mount目錄下面執行 write以后觸發的一個函數調用序列,圖中省略了VFS層的函數調用。fuse_file_aio_write 是在 mount 過程中注冊至U fuse_file_operations.aio_write 的函數指針,它會調 用 fuse_perform_write , fuse_perform_write 調用 get_fuse_conn 得至U struct fuse_conn 實例 fc,它 保存在 struct super_block 的私有數據成員中s_fs_info 中,而 struct super_bloc
26、k 是 struct inode 的一個成員。接下來是循環從用戶空間拷貝數據到內核,數據實際保存在struct pages中,內核fuse_req保存了 pages 指針,然后調用 fuse_send_write_pages。圖8魁,31lHet二卜 L KLFuse_send_write_pages調用會等待臟數據寫回到磁盤上,然后調用fuse_write_f川將包括操作碼FUSE_WRITE在內的信息寫入 request。隨后fuse_request_send(fc, req),它先通過fuse_get_unique獲取唯一請求號,請求號是一個64位無符號整數,請求號從1開始隨請求依次遞增
27、。然后調用queue_request(fc, req),它主要完成4件事情:1 .將request->list插入fc維護的pending鏈表尾部2 .置 req->state 為 FUSE_REQ_PENDING3 .wake_up喚醒等待隊列 fc->waitq4 .kill_fasync異步通知用戶進程數據到達從queue_request返回以后調用 request_wait_answer:進程被投入睡眠,等待請求完成 (wait_event(req->state = FUSE_REQ_FINISHED) 。如果用戶程序處理完了請求,它會 reply,進程被喚醒,
28、到此可以向上層調用返回處理結果 (錯誤碼或者寫入字節數)。在第6節我們提到了用戶空間有個daemon進程會循環read設備文件/fuse/dev以便處理內核請求,圖9展示了該read調用觸發的函數調用序列。圖9從第4節可知,FUSE模塊加載過程注冊了對設備文件/dev/fuse的操作接口fuse_dev_operations。由此可知,read 底層實際調用的是fuse_dev_readfuse_dev_read 首先通過 fuse_get_conn 獲彳導 struct fuse_conn 的實例 fc,通過 fuse_copy_init 為struct fuse_copy_state分配內
29、存并將其實例化。主要的數據讀取在fuse_dev_do_read中分4步完成:1 .request_wait:在掛起的列表上等待一個請求到達:(1) .DECLARE_WAITQUEUE(wait, current):創建等待隊列項,并將其初始化為current(2) .add_wait_queue_exclusive(&fc->waitq, &wait): 將 wait 力口入 fc->waitq,當有請求發送 到FUSE文件系統時,這個等待隊列上的進程會被喚醒(3) .如果沒有request, 一直循環檢查pending list和interrupt list,直到有請求;如果有請求則將 state設置為TASK_RUNNING(4) .將wait從等待隊列中移除2.list_entry(fc->pending.next, struct fuse_req, list) : 從 fc->pending.next 中取出 request, req-
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 審題技巧2024西醫臨床試題及答案
- 小學道法情景試題及答案
- 系統規劃與管理師考試試題及答案在線分析
- 德陽社工面試題及答案
- 系統架構設計師工作案例分析試題及答案
- 從業育嬰師的職業技能剖析試題及答案
- 燃氣工業爐試題及答案
- 疫情數學試題及答案大全
- 圖書管理員網絡應用能力試題及答案
- 群體瑜伽考試試題及答案
- (一模)桂林市、來賓市2025屆高考第一次跨市聯合模擬考試英語試卷(含答案詳解)
- 2025深圳市房產交易居間合同
- 2023年鄭州軌道工程職業學院單招職業適應性測試題庫附答案
- 2024北京理工大附中高一(下)期中英語試題及答案
- 玉盤二部合唱簡譜
- 中藥調劑處方審核考試題
- 故事派文案培訓課件(成都知了)
- 企業重組 特殊性稅務處理
- 裝修單項項目確認單
- 華為員工準則手冊
- 2020版中國阿爾茨海默病癡呆診療指南(全文)
評論
0/150
提交評論