之前面試時被問到描述下一個請求的完整流程,當時的結果很不理想,今天嘗試重新組織下,記錄在這裡。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiATN381dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yNwYDN1cTNwEjMmNWNzYmYyYzX5MDO1EDMyAzLcdDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
ceph-fuse1.png
這裡有篇文章通俗易懂地描述了VFS層頁緩存在cephfs中會有哪些“坑”以及相應政策。
mount後發生了什麼?
ceph-fuse在不指定rootpath參數時,client端的root inode和mds的root一樣。如果指定了rootpath參數,那麼client端的root inode則是rootpath中的最後一個dentry的inode,不過從這個inode的往上一直到mds root inode的路徑上的所有inode也都會在client端存一份副本(下圖紅色圓圈節點),這些父節點是為了計算quota時使用,比如quota可能設定在了mds root inode上,這時client端如果沒有mds root inode則無法正确得到quota值,除此之外這些父節點inode别無他用。
rootpath=/dir時
ceph-fuse接收到的請求一定是關于一個inode的,這點可以通過
Client::ll_xxx
函數定義看出。而且這個inode必須是已經從mds擷取過的。舉例來說,剛mount完成時,client端的中繼資料資訊隻有root inode(及其父節點inode),這時FUSE是不會直接向client(即ceph-fuse)請求一個非root inode直接子節點的,這一點是由VFS+FUSE子產品保證的。
打開檔案
通過
open()
系統調用打開檔案時,傳入參數是一個路徑,FUSE子產品會從root節點一個dentry一個dentry地周遊(通過
Client::ll_lookup()
)路徑,確定每個節點對應的inode都已經存在client端。如果在一個剛mount好的client中請求一個目錄深度很深的路徑,在系統調用上看隻是一個請求,但實際上在client和mds間會産生多次通信,路徑越深通信往返次數越多。
打開檔案是需要給
open()
傳入
flat_t
表示讀寫權限,這些系統flag在client端會先轉換成
CEPH_FILE_MODE_xxx
,然後每個
CEPH_FILE_MODE_xxx
對應到一組CAP
CEPH_CAP_xxx
,如果client已經擁有所需CAP,則成功傳回,否則向mds發起請求。
client側看open()
在隻有一個client時,client會作為loner擁有全部cap,這種情況比較簡單,通常在一次請求内就能完成。稍微複雜些的是多個client的情況。
第二個client調用open(path,O_RDWR)時mds中的處理
當client2 向mds發送OP_OPEN後,MDS端由
Server::handle_client_open()
來進行處理。處理過程主要是圖中的四步。
第一步加鎖是正常操作,為了防止父節點被删除。
第二步
issue_new_caps()
在mds端記錄client2 聲稱需要的cap,通過
eval()
驅動鎖的狀态進行轉換,因為有新的client加入,且兩個client都需要對檔案進行寫操作,這時IFILE lock從之前的EXCL狀态向MIX轉換,在本例的情況下(client1成功打開檔案後,client2發起打開請求),EXCL到MIX的狀态無法直接在MDS端完成,因為client1在之前作為loner被授予了過多cap,這些cap要先收回,才能繼續向MIX轉換。回收cap的是異步的,是以不會阻塞後續步驟。
第三步
check_inode_max_size()
是為了記錄client2 對檔案可寫入的範圍,這個資料作為client range被記錄到日志中,用于故障恢複時使用。這一步需要對IFILE進行
wrlock
,這次加寫鎖時不會有問題的,因為根據狀态機,EXCL->MIX狀态下是允許EXL角色進行
wrlock
的,剛好client1作為loner是XCL角色,是以加鎖不會失敗。等日志落盤後鎖會被釋放,這時再根據目前狀态判斷是否發送新的cap給各個client,如果這時對client1的revoke cap還未完成,那麼不會有新的cap發送給client,因為鎖的狀态沒有改變,需要繼續等對client1 cap的收回(當然這個等待是異步的)。整個第三步也不會阻塞後續步驟。
第四步将inode資訊發回給client2。隻有inode資訊,沒有cap,cap會在其他過程中單獨發給client2(當mds完成對client1 的cap revoke時)。
讀寫檔案
檔案打開後在系統層隻是擁有了檔案句柄,在實際調用
read()
或
write()
前,檔案的CAP可能還并沒有完全授予給client,是以不論是
Client::ll_read()
還是
Client::ll_write()
都會先進行
get_caps()
,確定已經有相應cap,如果沒有,就等待(通常情況下并不會向MDS發請求,因為沒有cap說明之前的
open()
調用還沒有真正結束,
open()
調用完成時需要有兩個結果:1.inode資訊被發回 2.cap被授予)。
讀寫檔案需要的cap包括:
cap | 用途 |
CEPH_CAP_FILE_RD | 讀檔案需要 |
CEPH_CAP_FILE_WR | 寫檔案需要 |
CEPH_CAP_FILE_CACHE | 從本client的cache中讀檔案需要 |
CEPH_CAP_FILE_CACHE | 從本client的cache中寫檔案需要 |