tmpfs 挂载后之前占用目录的进程无法发现刚 mount 目录中的文件 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
rev1si0n
V2EX    Linux

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

  •  
  •   rev1si0n 2023-11-07 10:16:57 +08:00 1490 次点击
    这是一个创建于 750 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题:在终端 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 
    27 条回复    2023-11-08 12:41:20 +08:00
    maybeonly
        1
    maybeonly  
       2023-11-07 10:20:14 +08:00
    可能需要在挂载点变动后重新 cd 进去一下
    timewarp
        2
    timewarp  
       2023-11-07 10:21:49 +08:00
    尽管终端 B 在 workdir 上挂了个文件系统,遮蔽了原来的目录视图,但是终端 A 的 shell 的 cwd 仍然指向原始视图,所以 A 只能看到原来的文件列表,你只需要做一次 cd ../ && cd -就能重新做一次路径查找,cwd 会遵循新的视图而设定。
    rev1si0n
        3
    rev1si0n  
    OP
       2023-11-07 10:22:27 +08:00
    @maybeonly 感谢解答。更新了一下问题,且不能重新 cd ♀
    zhlxsh
        4
    zhlxsh  
       2023-11-07 10:23:39 +08:00 via iPhone
    1. 重新 cd 一下
    2. b 操作在后的话,mount 会把 a 创建的文件屏蔽掉,umount 才恢复
    ho121
        5
    ho121  
       2023-11-07 10:27:44 +08:00
    直接 `bash` 重开一个子 shell
    timewarp
        6
    timewarp  
       2023-11-07 10:27:45 +08:00
    @rev1si0n 那就直接敲命令 ls /tmp/workdir 就行了,核心就是你当前这个 A 的 bash 进程的 cwd 已经和原始 workdir 的目录项绑定了,尽管这个目录项现在成了挂载点,但是不重新做一次路径查找的话内核是无法知道这个变动的,要想进行一次路径查找,又不想 cd 目录,那就只能让其他进程代劳了,比如 ls 一下/tmp/workdir
    mokiki
        7
    mokiki  
       2023-11-07 10:29:12 +08:00
    你先说出原始需求,才有可能给出替代方法
    zbinlin
        8
    zbinlin  
       2023-11-07 10:29:39 +08:00
    感觉是 X-Y 问题,你原来的问题是什么?
    rev1si0n
        9
    rev1si0n  
    OP
       2023-11-07 10:34:38 +08:00
    @maybeonly
    @timewarp
    @zhlxsh

    感谢解答。这是实际运行程序中的一个问题,我试一下在主进程重新直接 chdir 是否可行,因为有些对象可能已经占有了该目录的**引用**
    timewarp
        10
    timewarp  
       2023-11-07 10:42:27 +08:00
    @rev1si0n 无须担心其他进程对此目录的“占用”,这个在内核层面只是加了个引用计数,效果只是删除目录时内核里的数据结构不释放。对路径查找没有影响的,只要重新做查找就会看到新内容。
    rev1si0n
        11
    rev1si0n  
    OP
       2023-11-07 11:00:46 +08:00
    @mokiki
    @zbinlin
    @timewarp

    就是重新 cd 这一步可能做不到,因为那部分在第三方 sdk 里,第三方 sdk 在程序开始时就 opendir 了(作为某个静态的对象),所以可能一直使用的都是我 mount 之前的目录结构。刚浅试了一下似乎主进程去 chdir 到 mount 的目录再回来不会影响到这个已经占有 dir 的对象但是又不能重启主进程...
    timewarp
        12
    timewarp  
       2023-11-07 11:31:41 +08:00
    @rev1si0n 所以你的需求是 sdk 看到的是旧内容,但是主进程看到的是新内容,你也想让 sdk 现在看到主进程看到的新内容吗
    rev1si0n
        13
    rev1si0n  
    OP
       2023-11-07 11:35:08 +08:00
    @timewarp 对的
    BlackHole1
        14
    BlackHole1  
    PRO
       2023-11-07 12:48:45 +08:00
    尝试挂载成 overlayfs 呢
    julyclyde
        15
    julyclyde  
       2023-11-07 12:52:10 +08:00
    @timewarp ls 不可能有效吧
    timewarp
        16
    timewarp  
       2023-11-07 12:56:13 +08:00 via Android
    @julyclyde 有效的,只要重新做路径查找就能看到新的
    timewarp
        17
    timewarp  
       2023-11-07 12:56:44 +08:00 via Android
    @rev1si0n 你一定要在目录上挂载一个文件系统吗
    julyclyde
        18
    julyclyde  
       2023-11-07 12:57:53 +08:00
    @timewarp 这一定是你的幻觉
    timewarp
        19
    timewarp  
       2023-11-07 13:00:50 +08:00 via Android
    为今之计建议再建立一层目录,形成/tmp/common/workdir 的结构,主进程和 sdk 都切换到 common 这一层,然后主进程在 workdir 挂载文件系统,sdk 每次读取时去子目录 workdir 下找文件。这样不管 workdir 怎么被遮盖,sdk 每次都要做目录查找,看到的一定是最新的
    timewarp
        20
    timewarp  
       2023-11-07 13:05:14 +08:00 via Android
    @julyclyde `ls . `这个命令不会做路径查找,而是直接获取进程 cwd ,所以看到的是老内容,但是 ls /tmp/workdir 需要解析路径,在 open 系统调用时会做 walk path ,路径分量解析到 workdir 这一层发现 dentry 设置了挂载标志,表示这是个挂载点,于是路径查找下降到子文件系统,就看到了新内容。
    julyclyde
        21
    julyclyde  
       2023-11-07 13:51:25 +08:00
    @timewarp 建议了解一下/proc/pid/mounts
    你可以单独发帖子说一下你的过程,我去那边指出你的错误。在这里有点跑题了
    LindsayZhou
        22
    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
        23
    timewarp  
       2023-11-07 16:24:30 +08:00   2
    @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
        24
    LindsayZhou  
       2023-11-07 16:34:59 +08:00
    @LindsayZhou 续 #22
    挂个 eBPF 程序进到内核空间把程序的 pwd 都改了 (狗头
    rev1si0n
        25
    rev1si0n  
    OP
       2023-11-07 16:47:19 +08:00
    @julyclyde
    @timewarp
    @LindsayZhou

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

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

    如果程序里写死了”本地目录下某文件“那就无论如何也不可能让它访问到新的 mount 了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     912 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 22:19 PVG 06:19 LAX 14:19 JFK 17:19
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86