tmpfs 挂载后之前占用目录的进程无法发现刚 mount 目录中的文件

2023-11-07 10:16:57 +08:00
 rev1si0n

问题:在终端 A 中 ls -l 只能发现之前创建的两个文件,并不能发现 file03 ,请问,是否有办法在不结束终端 A 且不切换目录的情况下,在终端 A 中显示 file03 ?如果不行,是因为什么?或者,是否有其他替代方法?谢谢各位大佬解答。

过程:开启两个终端

终端 A:

mkdir -p /tmp/workdir
cd /tmp/workdir
touch file01
touch file02
ls -la

终端 B:

sudo mount -t tmpfs tmpfs /tmp/workdir
cd /tmp/workdir

touch file03
ls -la
1085 次点击
所在节点    Linux
27 条回复
julyclyde
2023-11-07 13:51:25 +08:00
@timewarp 建议了解一下/proc/pid/mounts
你可以单独发帖子说一下你的过程,我去那边指出你的错误。在这里有点跑题了
LindsayZhou
2023-11-07 16:24:17 +08:00
@rev1si0n 不太了解内核,如果答错请指正

如果只是说 cd 这个命令,那只要能调用 chdir syscall 的其他命令都一样的。如果说 chdir 系统调用都不能用,大概率是无解的吧。

bash 启动子进程的时候,会继承 bash 进程的工作路径。
这个数据存储在 bash 进程的 current(struct task_struct *) -> fs(struct fs_struct *) -> pwd(struct path) 里,struct path 有两个成员 vfsmount 和 dentry ,都是和文件系统强相关的。
vfsmount 直接就是文件系统的挂载信息,而 dentry 里有 inode 之类各个文件系统独立的信息,不修改 pwd 对象大概是不行的。
timewarp
2023-11-07 16:24:30 +08:00
@julyclyde 你真的是.....那我来给你讲讲代码吧...

首先,你讲的/proc/pid/mounts 是 pid 这个进程所在的命名空间里挂载的所有文件系统列表,跟题主的问题没有任何关系,题主没有涉及 namespace 的切换,主进程和 sdk (子进程之类的)处于同一个 namespace 。

其次,让我们看一下 vfs 层的代码,

path_lookupat 函数负责解析路径分量,path_init 负责初始化路径分量的解析起点,对于 ls . 这个命令来讲,我们把起点设定为 fs->pwd ,也就是/proc/pid/cwd 的值。放在题主的环境里起点就是 workdir 这个父目录
然后函数进入 link_path_walk 开始正式解析路径分量,由于我们的入参 name=".",所以此函数一个循环直接结束,不会进一步进入 walk_component 函数了。link_path_walk 返回 0 ,此时入参 nd 直接把父目录 workdir 带回了。上层函数 path_lookupat 直接调用 lookup_last 把父目录相关的 dentry 和 inode 准备好,然后层层返回,路径查找结束。

那么再来看看 ls /tmp/workdir 的情况,路径分量解析起点是/,即父文件系统的根目录。然后 name="/tmp/workdir"被 link_path_walk 逐段解析,由于/后是 tmp 字符串,所以调用 walk_component 首先解析 tmp ,这个环节无事发生,再然后使用 walk_component 继续进入 workdir 这个子目录,此时发现 workdir 的 dentry 上有个标志位 DCACHE_MOUNTED(mount 系统调用是给 workdir 这个挂载点设置的,参见函数 d_set_mounted),这说明了什么?说明这是个挂载点,于是 lookup_mnt 被调用,路径查找流程开始“下降”到子文件系统,所以我们要解析的下一个分量不再是父文件系统的 workdir 目录,而是子文件系统的根目录。于是我们就看到了新的内容。


对比以上两个过程,我们会发现当 ls . 的时候,由于. 是个特殊的分量,内核会特殊的处理,所以不会走 walk_component ,也就没机会检测到当前目录上的 DCACHE_MOUNTED 标志。
而我们 ls 一个/tmp/workdir 的时候,迫使内核重新走一遍路径分量解析,它就能发现 DCACHE_MOUNTED 标志。

这就是为什么 ls . 永远看到旧内容,而 ls ../workdir 或者 ls /tmp/workdir 却可以看到新内容
LindsayZhou
2023-11-07 16:34:59 +08:00
@LindsayZhou 续 #22
挂个 eBPF 程序进到内核空间把程序的 pwd 都改了 (狗头
rev1si0n
2023-11-07 16:47:19 +08:00
@julyclyde
@timewarp
@LindsayZhou

好了谢谢各位佬,学的也太扎实了也。我觉得这可能是不大可能了😂,佬们不要吵了吧
julyclyde
2023-11-08 12:38:16 +08:00
@timewarp 经实验,你说的是对的
我忘记全路径这个事了

cwd 的情况(也就是早已打开了旧目录的情况)确实无解。ls 相当于另外打开一次,是可以走全流程的
julyclyde
2023-11-08 12:41:20 +08:00
@timewarp 关键问题是
虽然 ls 完整路径可以看到新内容
但是 cwd 依然还是旧路径啊。这个方法并不能解决 OP 的提问“不结束终端 A 且不切换目录的情况下“

如果程序里写死了”本地目录下某文件“那就无论如何也不可能让它访问到新的 mount 了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/989378

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX