Open2
FUSE周りの情報整理

polyfuse
の開発にあたってかき集めた情報をちまちままとめておきたい
情報源
-
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/fs/fuse?h=v5.9.12
- カーネル側の実装
-
https://github.com/libfuse/libfuse/tree/fuse-3.10.0
- ユーザ空間側のリファレンス実装
- 非特権マウントで必要になる
fusermount
コマンドのソースもここにある
-
https://www.usenix.org/system/files/conference/fast17/fast17-vangoor.pdf
- FUSEのアーキテクチャについてある程度まとめられている
- https://www.kernel.org/doc/html/latest/filesystems/fuse.html
- https://man7.org/linux/man-pages/man8/fuse.8.html

多重化・マルチスレッド化に関する覚え書き
リクエストを多重化して処理したい場合、FUSEカーネルドライバとの接続であるファイルディスクリプタを共有したり複製したりして持ち回す必要がある。大雑把に考えられる選択肢は以下の通り:
1. 同じ fd を共有する場合
dup(2)
によるファイルディスクリプタの複製は実行せず、ひとつのFDを使いまわす
fuser
ではこの方針を採用している: https://github.com/cberner/fuser/blob/v0.15.1/src/channel.rs#L53 ほかの FUSE も多分同じ方針を採用してると思われる
イメージ
let fd = Arc::new(unsafe { OwnedFd::from_raw_fd(fd) });
let req = read_request(&fd)?;
///
let fd2 = fd.clone();
std::thread::spawn(move || {
req.reply_to(&fd2, ...);
});
利点
- 考えるべきことが少なくなり、実装がシンプルになる
欠点
- 大量のデータを扱う場合(例えばページキャッシュまるごと
read(2)
で呼び出したり、数GBもするデータをwritev(2)
で書き出すなど)、排他制御のせいで他スレッドがブロックされる-
splice(2)
による回避策などを取るべき
-
dup(2)
による複製
2. やってることは実質的に 1. と同じになる(はず)。ここでは説明を省略
FUSE_DEV_IOC_CLONE
3. /dev/fuse
をオープンした後、ioctl(newfd, FUSE_DEV_IOC_CLONE, &origfd)
によってコンテキストを共有する方式。
dup(2)
の場合とは異なり、複製後の newfd
は元の origfd
とは別の fd として扱われる
利点
- 排他制御によりブロッキングされない
欠点
- リプライを書き込む fd をデーモン側で気にする必要がある
-
read(2)
/readv(2)
などでリクエストを読み込むと、FUSEデバイスは対応するリクエストを 自身の processing queue へと移動させ、応答を待機する - ここで他の fd にリプライをしてしまうと、上で移動した processing queue に対する処理が元 fd 側で実行されなくなる
-