我正在實現一個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。
如果有人對此有任何洞見,將不勝感激!
非常好的問題,我也很感興趣。也許將它發佈在FUSE開發人員郵件列表上也是有用的。 – rralf
對不起rralf,我只是看到了這個。我已經發布在郵件列表上,最近得到了答覆,我會很快更新這個問題。 – abulford