Linux

綁定掛載上的 umount -R 花費了不可忽略的時間,為什麼?

  • February 8, 2018

為什麼umount -R需要 0.2 秒來解除安裝綁定掛載的子樹?掛載子樹只用了 0.02 秒,更改其傳播標誌只用了 0.00 秒。

(我正在考慮將目前的安裝集複製到一個子目錄,更改它們,然後使用 切換到它們pivot_mount。但是我觀察到的延遲對於這個目的來說實際上是不可接受的)。

本練習假定/子掛載是共享掛載。Linux 預設情況下不會這樣做,但systemd 會這樣做

# 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 -Rperf record -a、和perf report中顯示了幾個熱點。這個過程根本沒有出現。這可以解釋為什麼顯示在核心或使用者空間中花費的 CPU 時間可以忽略不計。gsd-housekeeping``gvfs-udisks2-volume-monitor``systemd``umount``time``umount

(如果有人在測試期間有更全面的方法來匯總每個程序的 cpu 使用情況,我會非常感興趣:)。

其他程序可能正在做一些處理以響應每個掛載事件。

例如,systemd 似乎負責使用我的四個 cpu 之一的 0.13s,在執行過程中需要 0.4s:

# 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程序(和 perf 本身)。

# 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

因此,您認為umount花費時間等待某事(因為它在user或中花費了很少的 cpu 時間sys)。讓我們找出它等待的原因……

# 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()

引用自:https://unix.stackexchange.com/questions/418100