Como entender a saída do rastreio executando um comando externo no bash?

1

No Ubuntu, executo date diretamente em um shell bash interativo cujo pid é 6913.

$ date
Wed Mar  2 23:57:44 EST 2016

Ao mesmo tempo, eu rastreio o shell bash 6913 de outro shell bash interativo usando strace :

    $ sudo strace -f -e trace=process -p 6913
    Process 6913 attached
    clone(Process 9098 attached
    child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
    child_tidptr=0x7f457c05ca10) = 9098
    [pid  6913] wait4(-1,  <unfinished ...>
    [pid  9098] execve("/bin/date", ["date"], [/* 66 vars */]) = 0
    [pid  9098] arch_prctl(ARCH_SET_FS, 0x7f40d6a4f740) = 0
    [pid  9098] exit_group(0)               = ?
    [pid  9098] +++ exited with 0 +++
    <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WSTOPPED|WCONTINUED, NULL) = 9098
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9098, si_status=0, si_utime=0, si_stime=0} ---
    wait4(-1, 0x7ffea6781518, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)
    clone(Process 9099 attached
    child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 9099
    [pid  9099] clone(Process 9100 attached
    child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 9100
    [pid  9099] wait4(-1,  <unfinished ...>
    [pid  9100] execve("/bin/sed", ["sed", "s:\([^/]\)[^/]*/:\1/:g"], [/* 66 vars */]) = 0
    [pid  9100] arch_prctl(ARCH_SET_FS, 0x7f998bb03840) = 0
    [pid  9100] exit_group(0)               = ?
    [pid  9100] +++ exited with 0 +++
    [pid  9099] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 9100
    [pid  9099] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9100, si_status=0, si_utime=0, si_stime=0} ---
    [pid  9099] wait4(-1, 0x7ffea6780c58, WNOHANG, NULL) = -1 ECHILD (No child processes)
    [pid  9099] exit_group(0)               = ?
    [pid  9099] +++ exited with 0 +++
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9099, si_status=0, si_utime=0, si_stime=0} ---
    wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, NULL) = 9099
    wait4(-1, 0x7ffea6780f18, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)

Parece-me que a saída pode ser dividida em duas partes:

  1. Primeiro o shell do bash 6913 clone() para criar um subprocesso     9098 e, em seguida, o subprocesso 9098 execve() date e sai.

  2. Depois disso, o shell bash 6913 clone() cria uma     subprocesso 9099, em seguida, o subprocesso 9099 clone() próprio para criar     um sub-processo 9100 e, em seguida, o subsubprocesso 9100 execve() sed . A minha pergunta é sobre esta segunda parte:

    • Os últimos dois clone() e o último execve() pertencem à ação que manipula o SIGCHLD recebido pelo bash shell 6913? O que a ação faz?

    • Por que processar 9100 execve() sed ? O que o sed faz aqui?

    • Por que o processo 9099 não é o processo que execve() sed ? Por que 9099 clone() para criar 9100 e, em seguida, 9100 execve() sed ? Em outras palavras, por que precisamos de dois clones sequenciais 9099 e 9100, em vez de apenas um clone 9099?

Para responder ao comentário:

$ echo $PROMPT_COMMAND
pwd2=$(sed "s:\([^/]\)[^/]*/:/:g" <<<$PWD)
$ echo $PS1
\u@\h:$pwd2\$

Depois de executar unset PROMPT_COMMAND no shel 6913, a saída de rastreio é

$ sudo strace -f -e trace=process -p 6913
[sudo] password for t: 
Process 6913 attached
clone(Process 12918 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 12918
[pid  6913] wait4(-1,  <unfinished ...>
[pid 12918] execve("/bin/date", ["date"], [/* 66 vars */]) = 0
[pid 12918] arch_prctl(ARCH_SET_FS, 0x7ff00c632740) = 0
[pid 12918] exit_group(0)               = ?
[pid 12918] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WSTOPPED|WCONTINUED, NULL) = 12918
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12918, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, 0x7ffea6781518, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)

Portanto, as duas primeiras perguntas acima são agora respondidas. Ainda não tenho certeza sobre a terceira questão.

    
por Tim 03.03.2016 / 07:43

1 resposta

1

clone(Process 9099 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 9099
[pid  9099] clone(Process 9100 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 9100
[pid  9099] wait4(-1,  <unfinished ...>
[pid  9100] execve("/bin/sed", ["sed", "s:\([^/]\)[^/]*/:\1/:g"], [/* 66 vars */]) = 0

Os dois garfos (em sistemas Linux modernos, garfos são feitos usando a chamada de sistema clone ) são devidos a bash avaliar sua variável PROMPT_COMMAND , que você disse ser

pwd2=$(sed "s:\([^/]\)[^/]*/:/:g" <<<$PWD)

Os garfos não estão diretamente relacionados ao sinal SIGCHLD recebido anteriormente.

O manual do bash diz:

The value of the variable PROMPT_COMMAND is examined just before Bash prints each primary prompt. If PROMPT_COMMAND is set and has a non-null value, then the value is executed just as if it had been typed on the command line.

Internamente, o bash finalmente chama parse_and_execute para avaliar o conteúdo de PROMPT_COMMAND . O Bash tenta minimizar o número de garfos que precisa fazer. Para instruções simples, como pwd2=$PWD , não são necessários garfos. Para declarações mais complexas, um ou mais garfos podem ser feitos. No seu caso, $( ... ) resulta em um fork, pid 9099, do shell, que então avaliará o comando entre parênteses. A chamada para o utilitário não integrado sed resulta em outro fork, pid 9100, seguido por um execve de /bin/sed .

What does sed do here?

Parece que corta todos, exceto o primeiro caractere do nome de cada diretório acima do diretório de trabalho atual.

    
por 03.03.2016 / 19:46