绑定挂载上的“umount -R”需要花费不可忽略的时间,为什么?

绑定挂载上的“umount -R”需要花费不可忽略的时间,为什么?

为什么umount -R需要 0.2 秒才能卸载这个绑定挂载的子树?挂载子树只需要 0.02 秒,更改其传播标志只需要 0.00 秒。

(我正在考虑将当前的安装集克隆到一个子目录,更改它们,然后使用 切换到它们pivot_mount。但我观察到的延迟对于此目的来说实际上是不可接受的)。

本练习假设/子挂载是共享挂载。 Linux 默认情况下并没有这样做,但是系统做。

# mkdir /mnt/a
# mount --bind /mnt/a /mnt/a --make-private
# time mount --rbind / /mnt/a
0.00user 0.00system 0:00.02elapsed 9%CPU (0avgtext+0avgdata 3020maxresident)k
0inputs+0outputs (0major+135minor)pagefaults 0swaps
# time mount --make-rprivate /mnt/a
0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 3184maxresident)k
0inputs+0outputs (0major+136minor)pagefaults 0swaps
# time umount -R /mnt/a
0.00user 0.00system 0:00.19elapsed 9%CPU (0avgtext+0avgdata 3392maxresident)k
0inputs+0outputs (0major+194minor)pagefaults 0swaps

进一步测试

strace -cw在表演下运行

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 90.44    0.180113        5297        34           umount2
 ...

因此,除了指出最后一个操作需要 34 次单独umount2()调用,而其他操作仅包含一次mount()使用 MS_REC(递归)标志的对 的调用之外,这并不是非常具有启发性。与 的标题数据一样time,现在是挂钟时间。 strace -c显示系统时间(即内核花费的cpu时间),总共只有0.009s。

尽管它确实指出了一些有趣的事情。改为使用umount -l /mnt/a,可将总时间减少至 0.02 秒。这使用单个umount2()调用来分离子树/mnt/a(并在后台进行任何清理)。

从 的单次调用来看strace -ttt -T -e trace=umount2 umount -R /mnt/a,单次调用的次数分布比较均匀;它们的范围从 0.002s 到 0.012s,但没有明显的模式,如果我重复,模式看起来也不一致。


umount -R运行后perf record -aperf report显示gsd-housekeepinggvfs-udisks2-volume-monitor、 、中的几个热点systemd。进程umount根本就没有显示出来。这可以解释为什么在内核或用户空间中花费的 CPU 时间可以忽略不计timeumount

(如果有人有更全面的方法来聚合测试期间每个进程的 cpu 使用情况,我会非常感兴趣:)。

其他进程可能正在执行一些处理以响应每个安装事件。

例如,在一次耗时 0.4 秒的运行中,systemd 似乎负责使用我的四个 cpu 之一的 0.13 秒:

# systemctl set-property init.scope CPUAccounting=yes
# systemctl show --property CPUUsageNSec init.scope; time umount -R /mnt/a ; systemctl show --property CPUUsageNSec init.scope
CPUUsageNSec=2403124481

real    0m0.408s
user    0m0.015s
sys 0m0.020s
CPUUsageNSec=2534058385

# echo $(( 2534058385 - 2403124481 ))
130933904

但这似乎不是正确的解释,因为在私有挂载命名空间中运行时也会发生相同的延迟。在这种情况下,perf record -a不显示其他进程,仅显示umount进程(和性能本身)。

# unshare -m
# time mount --rbind / /mnt/a

real    0m0.005s
user    0m0.003s
sys 0m0.002s
# time mount --make-rprivate /mnt/a

real    0m0.005s
user    0m0.003s
sys 0m0.002s
# systemctl show --property CPUUsageNSec init.scope; time umount -R /mnt/a ; systemctl show --property CPUUsageNSec init.scope
CPUUsageNSec=3637792026

real    0m0.381s
user    0m0.026s
sys 0m0.018s
CPUUsageNSec=3645973005
# echo $((3645973005-3637792026))
8180979

在这种情况下,cpu 似乎并不相关。我有 4 个能够以 2.3Ghz 运行的 cpu 核心,但perf stat -a总体显示 cpu 使用率低于 5%。 (忽略“使用的CPU,我认为它总是显示-a使用时的完整值)。

# time perf stat -a umount -R /mnt/a

 Performance counter stats for 'system wide':

       2079.333650      cpu-clock (msec)          #    3.998 CPUs utilized          
               635      context-switches          #    0.305 K/sec                  
                23      cpu-migrations            #    0.011 K/sec                  
               333      page-faults               #    0.160 K/sec                  
       198,278,822      cycles                    #    0.095 GHz                    
       138,734,277      instructions              #    0.70  insn per cycle         
        31,401,067      branches                  #   15.102 M/sec                  
           934,327      branch-misses             #    2.98% of all branches        

       0.520083596 seconds time elapsed


real    0m0.543s
user    0m0.038s
sys 0m0.043s

然而,仍有一些进程响应此事件... umount 仍会在系统日志中触发 78 行消息。

Feb 07 10:34:26 alan-laptop systemd[1]: proc-sys-fs-binfmt_misc.automount: Got automount request for /proc/sys/fs/binfmt_misc, triggered by 6040 (umount)
Feb 07 10:34:26 alan-laptop systemd[1]: proc-sys-fs-binfmt_misc.automount: Automount point already active?
Feb 07 10:34:26 alan-laptop systemd[1]: proc-sys-fs-binfmt_misc.automount: Got automount request for /proc/sys/fs/binfmt_misc, triggered by 6040 (umount)
Feb 07 10:34:26 alan-laptop systemd[1]: proc-sys-fs-binfmt_misc.automount: Automount point already active?

注意findmnt显示我避免创建任何可怕的类似递归的传播,例如,如果我在以下命令之后运行它--make-rprivate

findmnt -o TARGET,PROPAGATION
TARGET                                          PROPAGATION
/                                               shared
├─/sys                                          shared
│ ├─/sys/kernel/security                        shared
│ ├─/sys/fs/cgroup                              shared
│ │ ├─/sys/fs/cgroup/unified                    shared
│ │ ├─/sys/fs/cgroup/systemd                    shared
│ │ ├─/sys/fs/cgroup/net_cls,net_prio           shared
│ │ ├─/sys/fs/cgroup/cpu,cpuacct                shared
│ │ ├─/sys/fs/cgroup/devices                    shared
│ │ ├─/sys/fs/cgroup/freezer                    shared
│ │ ├─/sys/fs/cgroup/perf_event                 shared
│ │ ├─/sys/fs/cgroup/hugetlb                    shared
│ │ ├─/sys/fs/cgroup/memory                     shared
│ │ ├─/sys/fs/cgroup/blkio                      shared
│ │ ├─/sys/fs/cgroup/cpuset                     shared
│ │ └─/sys/fs/cgroup/pids                       shared
│ ├─/sys/fs/pstore                              shared
│ ├─/sys/fs/selinux                             shared
│ ├─/sys/kernel/debug                           shared
│ └─/sys/kernel/config                          shared
├─/proc                                         shared
│ └─/proc/sys/fs/binfmt_misc                    shared
├─/dev                                          shared
│ ├─/dev/shm                                    shared
│ ├─/dev/pts                                    shared
│ ├─/dev/mqueue                                 shared
│ └─/dev/hugepages                              shared
├─/run                                          shared
│ ├─/run/user/1000                              shared
│ └─/run/user/42                                shared
├─/usr                                          shared
├─/tmp                                          shared
├─/boot                                         shared
└─/mnt/a                                        private
  └─/mnt/a                                      private
    ├─/mnt/a/usr                                private
    ├─/mnt/a/sys                                private
    │ ├─/mnt/a/sys/kernel/security              private
    │ ├─/mnt/a/sys/fs/cgroup                    private
    │ │ ├─/mnt/a/sys/fs/cgroup/unified          private
    │ │ ├─/mnt/a/sys/fs/cgroup/systemd          private
    │ │ ├─/mnt/a/sys/fs/cgroup/net_cls,net_prio private
    │ │ ├─/mnt/a/sys/fs/cgroup/cpu,cpuacct      private
    │ │ ├─/mnt/a/sys/fs/cgroup/devices          private
    │ │ ├─/mnt/a/sys/fs/cgroup/freezer          private
    │ │ ├─/mnt/a/sys/fs/cgroup/perf_event       private
    │ │ ├─/mnt/a/sys/fs/cgroup/hugetlb          private
    │ │ ├─/mnt/a/sys/fs/cgroup/memory           private
    │ │ ├─/mnt/a/sys/fs/cgroup/blkio            private
    │ │ ├─/mnt/a/sys/fs/cgroup/cpuset           private
    │ │ └─/mnt/a/sys/fs/cgroup/pids             private
    │ ├─/mnt/a/sys/fs/pstore                    private
    │ ├─/mnt/a/sys/kernel/config                private
    │ ├─/mnt/a/sys/fs/selinux                   private
    │ └─/mnt/a/sys/kernel/debug                 private
    ├─/mnt/a/dev                                private
    │ ├─/mnt/a/dev/shm                          private
    │ ├─/mnt/a/dev/pts                          private
    │ ├─/mnt/a/dev/mqueue                       private
    │ └─/mnt/a/dev/hugepages                    private
    ├─/mnt/a/run                                private
    │ ├─/mnt/a/run/user/1000                    private
    │ └─/mnt/a/run/user/42                      private
    ├─/mnt/a/proc                               private
    │ └─/mnt/a/proc/sys/fs/binfmt_misc          private
    ├─/mnt/a/tmp                                private
    ├─/mnt/a/boot                               private
    └─/mnt/a/mnt/a                              private

答案1

所以你认为umount花时间等待某事(因为它在 或 中花费很少的CPU时间usersys。让我们找出它为什么等待......

# perf trace -g -e sched:* umount2 -R /mnt/a

perf record向我们展示了几个调度程序跟踪点;事实证明,最能说明问题的是sched:sched_switch

Samples: 21  of event 'sched:sched_switch', Event count (approx.): 21
  Children      Self  Trace output                                                                                                                   ▒
-  100.00%   100.00%  umount:1888 [120] D ==> swapper/3:0 [120]                                                                                      ▒
     0                                                                                                                                               ▒
     __umount2                                                                                                                                       ▒
     entry_SYSCALL_64_fastpath                                                                                                                       ▒
     sys_umount                                                                                                                                      ▒
     do_umount                                                                                                                                       ▒
     namespace_unlock                                                                                                                                ▒
     synchronize_sched                                                                                                                               ▒
     __wait_rcu_gp                                                                                                                                   ▒
     wait_for_completion                                                                                                                             ▒
     schedule_timeout                                                                                                                                ▒
     schedule                                                                                                                                        ▒
     __schedule                                                                                                                                      ▒
     __schedule   

__wait_rcu_gp()指 RCU 宽限期。 namespace_unlock()infs/namespace.c是某种形式的全局同步,其中包括synchronize_rcu().它等到全部“当前执行的 RCU 读端关键部分已完成”。 “RCU 宽限期延长了数毫秒……这种情况是在以读取为主的情况下使用 RCU 的经验法则的主要原因”。我认为挂载命名空间被认为是“主要读取”。

看来这“几毫秒”占了 34 次调用中每次平均等待时间 5 毫秒的原因umount2()

相关内容