Comportamento estranho de '/ proc / self / environ' em alguns shells; o que está acontecendo?

7

Eu estou no Debian GNU / Linux 9. Eu sei que /proc é especial , eu sei que /proc/self é .

Este comando

sh -c '/bin/cat /proc/self/comm - </proc/self/comm'

rendimentos

cat
sh

O padrão será semelhante se eu usar dash em vez de sh . Mas com bash , ksh ou zsh , o resultado é

cat
cat

Tomando /proc/self/stat em vez de /proc/self/comm , posso confirmar que os dois cat -s são de fato o mesmo processo único. Aparentemente as conchas diferem sob o capô, tudo bem. Agora vamos dar

sh -c '/bin/cat /proc/self/environ - </proc/self/environ'

Tendo observado o acima, com sh ou dash , espero ver o ambiente do cat primeiro, o ambiente do shell mais tarde. Parece funcionar (ambos os ambientes são provavelmente idênticos, então é difícil dizer se tudo funciona como esperado, mas meu ponto é: nem environ está vazio).

Com bash , ksh ou zsh , espero ver o ambiente do cat duas vezes, mas é impresso apenas uma vez . Dividindo em dois casos separados:

  • bash -c '/bin/cat - </proc/self/environ' não imprime nada, como se environ estivesse vazio;
  • bash -c '/bin/cat /proc/self/environ' imprime algo como esperado.

O que está acontecendo? Este não é o caso de comm ou stat . Por que environ é diferente?

$ uname -a
Linux barbaz 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1 (2018-04-29) x86_64 GNU/Linux
    
por Kamil Maciorowski 06.09.2018 / 01:36

1 resposta

3

As diferenças entre os shells são devidas a diferenças na configuração do processo. dash configura os redirecionamentos antes de bifurcar, então /proc/self aponta para o shell; bash e zsh os configuram após o bifurcação, portanto, /proc/self aponta para o novo processo. Você pode ver isso acontecer com strace -f :

  • strace -f dash -c '/bin/cat /proc/self/comm - </proc/self/comm' mostra (entre muitas outras coisas)

    open("/proc/self/comm", O_RDONLY)       = 3
    fcntl(0, F_DUPFD, 10)                   = 10
    close(0)                                = 0
    fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
    dup2(3, 0)                              = 0
    close(3)                                = 0
    clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f12581299d0) = 7743
    strace: Process 7743 attached
    [pid  7742] wait4(-1,  <unfinished ...>
    [pid  7743] execve("/bin/cat", ["/bin/cat", "/proc/self/comm", "-"], [/* 43 vars */]) = 0
    

    ( /proc/self/comm é aberto antes da chamada do sistema clone , que é onde o processo se bifurca);

  • strace -f bash -c '/ bin / cat / proc / self / comm -

    clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fb506bdee10) = 8106
    strace: Process 8106 attached
    [... snip a ton of signal-handling setup ...]
    [pid  8106] open("/proc/self/comm", O_RDONLY) = 3
    [pid  8106] dup2(3, 0)                  = 0
    [pid  8106] close(3)                    = 0
    [pid  8106] execve("/bin/cat", ["/bin/cat", "/proc/self/comm", "-"], [/* 43 vars */]) = 0
    

    ( /proc/self/comm é aberto após a chamada clone , no processo filho, 8106).

Entender por que environ aparece vazio requer um pouco mais de explicação. Quando /proc/<pid>/environ é aberto , o kernel salva uma cópia do ponteiro para a tarefa mm_struct , que contém ponteiros para o ambiente. Mas execve , que é usado para iniciar o processo cat , cria um novo mm_struct para o processo . Assim, o redirecionamento acaba apontando para informações obsoletas e quando cat lê suas entradas, ele não vê seu ambiente real. O ambiente que ele faz deve ser uma cópia de seus pais, mas as camadas envolvidas limpam antes de bifurcar e configurar o novo ambiente (que é configurado por execve ).

    
por 06.09.2018 / 07:42