免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

SSHFS源码剖析③:cache.c元数据缓存内幕——超时、失效策略与线程安全

SSHFS源码剖析③:cache.c元数据缓存内幕——超时、失效策略与线程安全 SSHFS源码剖析③cache.c元数据缓存内幕——超时、失效策略与线程安全【免费下载链接】sshfsA network filesystem client to connect to SSH servers项目地址: https://gitcode.com/gh_mirrors/sshfs1/sshfsSSHFS 是一款基于 SFTP 的网络文件系统客户端能把任意 SSH 服务器上的目录挂载到本地直接使用。这是 SSHFS 源码剖析系列的第三篇聚焦元数据缓存模块 cache.c默认 20 秒的缓存超时如何计时、写操作会触发哪些失效策略、以及一把互斥锁如何保证多线程下的正确性。为什么 SSHFS 需要元数据缓存每次ls、stat都需要通过 SSH 隧道向远程发一次 SFTP 请求网络延迟会让看看目录里有什么都变得缓慢。SSHFS 的解法是在 FUSE 与 SFTP 之间插入一层元数据缓存代理把文件属性、目录列表、符号链接目标暂时存到内存里。这个代理默认就是开启的-o dir_cacheyes挂载时在 sshfs.c 中通过cache_wrap把原始操作表整体套壳if(sshfs.dir_cache) sshfs.op cache_wrap(sshfs_oper);也就是说read、write等数据操作原样直通而getattr、readdir、readlink等元数据操作被拦截先查缓存、未命中才走网络。这套代理 直通的分流逻辑集中在 cache_fill 中完成。缓存架构速览一张哈希表 一把锁 整个缓存模块的核心状态就是一个静态的 struct cacheGHashTable *table以文件路径字符串为键的哈希表pthread_mutex_t lock保护哈希表的互斥锁三类超时 两个清理参数下面详述write_ctr全局写计数器是失效策略的关键哈希表里每条记录是一个 struct node同时承载三种元数据各有独立的过期时间戳struct node { struct stat stat; /* 文件属性 */ time_t stat_valid; /* 属性过期时刻 */ char **dir; /* 目录项名称列表 */ time_t dir_valid; /* 目录列表过期时刻 */ char *link; /* 符号链接目标 */ time_t link_valid; /* 链接过期时刻 */ time_t valid; /* 三者中最晚的过期时刻 */ };对外只暴露了 5 个接口见 cache.h包装操作表、解析选项、写入属性、失效指定路径、获取写计数器——非常克制。超时机制默认 20 秒如何计时与过期 ⏱️cache_parse_options 中设定了四组默认值#define DEFAULT_CACHE_TIMEOUT_SECS 20 #define DEFAULT_MAX_CACHE_SIZE 10000 #define DEFAULT_CACHE_CLEAN_INTERVAL_SECS 60 #define DEFAULT_MIN_CACHE_CLEAN_INTERVAL_SECS 5以属性查询为例读路径是先查后取cache_get_attr 加锁查哈希表若stat_valid - now 0未过期直接返回缓存的stat未命中或过期则返回-EAGAIN外层 cache_getattr 收到-EAGAIN后真正向远程发起 SFTP 请求成功后调 cache_add_attr 把结果写入缓存stat_valid 当前时间 stat_timeout_secs。readdir和readlink同理分别使用dir_timeout与link_timeout。节点的整体有效期valid取三者最大值供清理逻辑判断这个节点还有没有价值。 一个巧妙的细节cache_readdir命中缓存时只回填文件名cache.c#L371-L379而真正从远程读目录时cache_dirfill 会顺手把每个子项的stat也缓存掉——一次ls -l就能预热一大片属性缓存。失效策略写操作后如何避免读到旧数据 ✅缓存过期只保证最终一致写入场景还需要主动失效否则改名、删文件后可能继续看到旧数据。模块内有三档失效函数函数动作cache_invalidate只删除该路径cache_invalidate_dir删除该路径 父目录cache_purge_parentcache_invalidate_write删除该路径并把全局write_ctr加一各写操作对应哪种失效规则很直观操作失效范围mkdir/mknod/create/unlink/rmdir该目录 父目录chmod/chown/utimens/truncate仅该文件write该文件 写计数器 1symlink/link目标目录硬链接额外失效源文件rename最彻底见下rename的 cache_do_rename 除了失效新旧路径和双方父目录还会用前缀匹配把旧路径下的所有子孙条目一并清掉cache_del_children防止旧目录的子项在缓存里幽灵残留。 关于write_ctr它解决的是并发竞争——A 线程读到写前计数器B 线程写入并 1A 再把查到的属性写回缓存时若发现计数器已变就放弃写入见 cache_add_attr避免用写入发生前的旧属性覆盖新状态。sshfs.c 的open路径也遵循同一协议开文件前先取计数器成功后回填属性、失败则失效该路径。缓存清理10000 条容量与 60 秒清理窗口 缓存条目只增会撑爆内存因此 cache_clean 内置了双门槛 双触发if (now cache.last_cleaned cache.min_clean_interval_secs (g_hash_table_size(cache.table) cache.max_size || now cache.last_cleaned cache.clean_interval_secs)) { g_hash_table_foreach_remove(cache.table, (GHRFunc) cache_clean_entry, now); ... }翻译成人话触发条件满足其一条目数超过max_size默认 10000或距上次清理超过clean_interval默认 60 秒限流保护无论如何两次清理间隔不能短于min_clean_interval默认 5 秒防止频繁遍历哈希表清理规则逐个检查节点now node-valid的整条删除。注意它是惰性触发——没有后台线程而是在每次写入缓存add_attr/add_dir/add_link时顺带调用实现成本极低。线程安全单锁设计如何兼顾性能与正确性 FUSE 是多线程分发请求的缓存模块的线程安全策略简单直接一把互斥锁罩住整个哈希表但把持锁区间压到最短读路径如 cache_get_attr锁内只做查表 拷贝stat拷完立即解锁重网络请求发生在锁外cache_readlink 命中时在锁内完成字符串拷贝后提前解锁返回所有失效/写入函数都遵循加锁 → 纯哈希表操作 → 解锁的短临界区模式。这种粗粒度单锁看似原始实则务实缓存里存的都是轻量元数据几十字节到几 KB 的字符串临界区以微秒计锁竞争远小于其带来的实现复杂度与出错风险——对一个正确性优先的挂载客户端来说这是教科书式的取舍。实战调参常用 dcache 选项速查表 ⚙️以下选项均可通过-o传给 sshfs帮助文本见 sshfs.c解析定义见 cache_optscache_*旧名仍兼容选项默认值作用dir_cacheyes/noyes整个元数据缓存总开关dcache_timeoutN20属性/目录/链接超时统一设置秒dcache_stat_timeoutN20仅文件属性超时dcache_dir_timeoutN20仅目录列表超时dcache_link_timeoutN20仅符号链接超时dcache_max_sizeN10000缓存最大条目数dcache_clean_intervalN60定时清理间隔秒dcache_min_clean_intervalN5容量超限时的最小清理间隔秒 调参建议远端文件频繁变动、又在意一致性时调小dcache_stat_timeout目录巨大上万文件且基本只读时可适当调大dcache_dir_timeout与dcache_max_size让ls更快。想跟读源码可以拉取仓库git clone https://gitcode.com/gh_mirrors/sshfs1/sshfs重点阅读顺序推荐cache.h 接口 → cache.c#L23-L48 数据结构 → cache.c#L526-L575 操作包装与初始化 → 各cache_*拦截函数。小结cache.c 用约 600 行代码以哈希表 单锁 三类超时 分级失效 惰性清理五件套为 SSHFS 这块网络文件系统补上了本地化体验的关键一环⏱️超时属性、目录、链接各自 20 秒过期节点整体有效期取最大值✅失效按操作类型精确失效自身、父目录乃至整棵子树配合write_ctr防并发脏写清理容量或时间双触发最小间隔限流无后台线程线程安全全局单锁 微秒级临界区简单且可靠。下一期我们将走进 sshfs.c 的多连接管理看看-o max_conns如何让多核机器榨干 SSH 带宽。【免费下载链接】sshfsA network filesystem client to connect to SSH servers项目地址: https://gitcode.com/gh_mirrors/sshfs1/sshfs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表