'umount -R' em montagens de ligação leva um tempo não-negligível, por quê?

0

Por que umount -R usa 0,2s para desmontar essa subárvore de montagens de ligação? Levou apenas 0.02s para montar a subárvore e 0.00s para alterar seus sinalizadores de propagação.

(Eu estava olhando para clonar o conjunto atual de montagens, para um subdiretório, alterando-as, depois mudando para elas usando pivot_mount . Mas o atraso que estou observando não seria realmente aceitável para o propósito).

Este exercício assume / e as submontagens são montagens compartilhadas. O Linux não os torna assim por padrão, mas systemd faz.

# 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

Testes adicionais

A execução sob strace -cw mostra

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

Portanto, não é super revelador, além de apontar que esta última operação requer 34% deumount2() de chamadas individuais, enquanto ambas as outras incluem apenas uma chamada para mount() , usando o sinalizador MS_REC (recursivo). Tal como acontece com os números de manchete de time , este é o tempo de relógio de parede. strace -c mostra a hora do sistema (ou seja, o tempo da CPU gasto no kernel), que tem apenas um total de 0,009s.

Embora aponte algo interessante. Usando umount -l /mnt/a , reduz o tempo total para 0,02s. Isso usa uma única chamada umount2() para desanexar a subárvore /mnt/a (e fazer qualquer limpeza em segundo plano).

Olhando para as chamadas individuais com strace -ttt -T -e trace=umount2 umount -R /mnt/a , a distribuição de tempos para chamadas individuais é relativamente uniforme; eles variam de 0,002s a 0,012s, mas não há um padrão claro, e o padrão não parece consistente se eu repeti-lo.

Depois de executar umount -R em perf record -a , perf report mostra vários pontos de acesso em gsd-housekeeping , gvfs-udisks2-volume-monitor e systemd . O processo umount não é exibido. Isso poderia explicar por que time mostrou umount gastando tempo de CPU negligente no kernel ou no espaço do usuário.

(Eu ficaria muito interessado se alguém tiver uma maneira mais abrangente de agregar o uso de cpu por processo durante um teste:).

Os outros processos provavelmente estão processando em resposta a cada evento de montagem.

Por exemplo systemd parece ser responsável por usar 0.13s de um dos meus quatro processadores, durante uma corrida que levou 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

Mas esta não parece ser a explicação correta, porque o mesmo atraso está acontecendo ao executar em um namespace de montagem particular. Nesse caso, perf record -a não mostra os outros processos, apenas o processo umount (e o próprio 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

A CPU não parece ser relevante neste caso. Eu tenho 4 núcleos de cpu capazes de rodar a 2.3Ghz, mas perf stat -a mostra menos de 5% de uso de cpu no geral. (Ignore "CPUs utilizadas, acho que sempre mostra o valor total quando -a é usado).

# 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

No entanto, ainda há algum processo respondendo a este evento ... O umount ainda dispara 78 linhas de mensagens no log do sistema.

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?

Note que findmnt mostra que evitei criar qualquer propagação horrível recursiva, por exemplo se eu executar depois do --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
    
por sourcejedi 18.01.2018 / 21:47

1 resposta

1

Então você acha que umount gasta tempo esperando por algo (já que gasta muito pouco tempo de CPU em user ou sys ). Vamos descobrir porque espera ...

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

perf record nos mostra atingindo vários pontos de rastreio do agendador; Acontece que a revelação é 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() refere-se a um período de cortesia da RCU. namespace_unlock() in fs/namespace.c é alguma forma de sincronização global, que inclui synchronize_rcu() . Ele espera até que todas "seções críticas executadas atualmente no lado de leitura do RCU sejam concluídas". "Os períodos de carência da UCP se estendem por vários milissegundos ... essa situação é uma das principais razões para a regra geral de que o RCU seja usado em situações de leitura geral". Suponho que os namespaces de montagem sejam considerados "lidos principalmente".

Parece que esses "poucos milissegundos" são responsáveis pela espera média de 5 milissegundos em cada uma das 34 chamadas para umount2() .

    
por 08.02.2018 / 16:05