2013-08-27 24 views
12

我正在實現一個FUSE文件系統,旨在通過熟悉的POSIX調用來訪問實際存儲在RESTful API後面的文件。文件系統首次檢索文件系統後會緩存文件,以便在隨後的訪問中更容易使用它們。FUSE getattr操作應該總是被序列化嗎?

我在多線程模式下運行文件系統(這是FUSE的默認設置),但發現getattr調用似乎被序列化,即使其他調用可以並行進行。

當打開一個文件FUSE總是首先調用getattr,並且我支持的客戶端需要這個初始調用返回的文件大小是準確的(我對這種行爲沒有任何控制權)。這意味着如果我沒有緩存文件,我需要通過RESTful API調用實際獲取信息。有時這些呼叫會發生在高延遲網絡上,往返時間大約爲600毫秒。

由於getattr調用的明顯順序性,任何對當前未緩存的文件的訪問都將導致整個文件系統在getattr服務期間阻止任何新操作。

我已經想出了很多方法來解決這個問題,但都看起來很醜或冗長,真的我只是想讓getattr調用像所有其他調用一樣並行運行。

查看源代碼我看不出爲什麼getattr應該像這樣工作,FUSE確實鎖定了tree_lock互斥鎖,但僅用於讀取,並且沒有寫入同時發生。

爲了在這個問題中發佈一些簡單的東西,我敲了一個令人難以置信的基本實現,它只是支持getattr並且可以輕鬆演示這個問題。

#ifndef FUSE_USE_VERSION 
#define FUSE_USE_VERSION 22 
#endif 

#include <fuse.h> 
#include <iostream> 

static int GetAttr(const char *path, struct stat *stbuf) 
{ 
    std::cout << "Before: " << path << std::endl; 
    sleep(5); 
    std::cout << "After: " << path << std::endl; 
    return -1; 
} 

static struct fuse_operations ops; 

int main(int argc, char *argv[]) 
{ 
    ops.getattr = GetAttr; 
    return fuse_main(argc, argv, &ops); 
} 

使用一對端子中的(大致)來調用的路徑上LS的同時顯示,一旦第一已完成第二GETATTR呼叫只啓動,這將導致第二LS採取約10秒而不是5.

終端1

$ date; sudo ls /mnt/cachefs/file1.ext; date 
Tue Aug 27 16:56:34 BST 2013 
ls: /mnt/cachefs/file1.ext: Operation not permitted 
Tue Aug 27 16:56:39 BST 2013 

終端2

$ date; sudo ls /mnt/cachefs/file2.ext; date 
Tue Aug 27 16:56:35 BST 2013 
ls: /mnt/cachefs/file2.ext: Operation not permitted 
Tue Aug 27 16:56:44 BST 2013 

如您所見,來自ls之前的兩個date輸出的時間差僅相差1秒,但ls之後的兩個輸出相差5秒,這對應於GetAttr函數中的延遲。這表明第二個呼叫在FUSE深處被阻塞。

輸出

$ sudo ./cachefs /mnt/cachefs -f -d 
unique: 1, opcode: INIT (26), nodeid: 0, insize: 56 
INIT: 7.10 
flags=0x0000000b 
max_readahead=0x00020000 
    INIT: 7.8 
    flags=0x00000000 
    max_readahead=0x00020000 
    max_write=0x00020000 
    unique: 1, error: 0 (Success), outsize: 40 
unique: 2, opcode: LOOKUP (1), nodeid: 1, insize: 50 
LOOKUP /file1.ext 
Before: /file1.ext 
After: /file1.ext 
    unique: 2, error: -1 (Operation not permitted), outsize: 16 
unique: 3, opcode: LOOKUP (1), nodeid: 1, insize: 50 
LOOKUP /file2.ext 
Before: /file2.ext 
After: /file2.ext 
    unique: 3, error: -1 (Operation not permitted), outsize: 16 

上面的代碼和實施例是不一樣的實際應用或應用程序如何被使用,但表明了相同的行爲。我沒有在上面的例子中顯示過,但是我發現一旦getattr調用完成,後續的打開的調用就可以並行運行,就像我期望的那樣。

我搜索了文檔,試圖解釋這種行爲,並試圖找到其他人報告類似的經驗,但似乎無法找到任何東西。可能是因爲getattr的大多數實現會非常快,所以你不會注意到它是否被序列化,或者因爲我在配置中做了一些愚蠢的事情。我使用的是FUSE 2.7.4版本,所以可能這是一個已經修復的舊bug。

如果有人對此有任何洞見,將不勝感激!

+0

非常好的問題,我也很感興趣。也許將它發佈在FUSE開發人員郵件列表上也是有用的。 – rralf

+1

對不起rralf,我只是看到了這個。我已經發布在郵件列表上,最近得到了答覆,我會很快更新這個問題。 – abulford

回答

10

我簽署了FUSE郵件列表,張貼了我的問題,最近得到了米克洛什Szeredi以下回應:

查找(即第一次發現與名稱相關的文件)每個目錄序列 。這是在VFS(內核中的通用文件系統 部分)中,所以基本上任何文件系統都容易受此問題影響,而不僅僅是保險絲。

非常感謝Miklos的幫助。完整的線程請參閱http://fuse.996288.n3.nabble.com/GetAttr-calls-being-serialised-td11741.html

我也注意到序列化是按目錄進行的,即如果兩個文件都在同一個目錄中,則會看到上述效果,但是如果它們在單獨的目錄中則不會。對於我的應用程序來說,這種緩解對我來說已經足夠了,我的文件系統的客戶端確實使用目錄,所以雖然我可能會接連接收很多getattr調用,但它們都發生在同一個目錄中的可能性已經足夠低了,擔心。

對於那些緩解力不夠的人,如果你的文件系統支持目錄列表,你可能會利用David Strauss的建議,即使用readdir調用作爲觸發器來填充你的緩存:

在我們的文件系統,我們嘗試預取並緩存READDIR在屬性 信息(這將不可避免地被要求),所以我們 不必打的每一個後端。

由於後臺到我的文件系統沒有目錄的概念,我無法利用他的建議,但希望這會對其他人有所幫助。