Como ler variáveis de ambiente de um processo

38

O /proc/<pid>/environ do Linux não é atualizado (pelo que entendi, o arquivo contém o ambiente inicial do processo).

Como posso ler o ambiente atual de um processo?

    
por Jonathan Ben-Avraham 14.01.2012 / 17:16

4 respostas

19

/proc/$pid/environ atualiza se o processo mudar seu próprio ambiente. Mas muitos programas não se incomodam em mudar seu próprio ambiente, porque é um pouco inútil: o ambiente de um programa não é visível através de canais normais, apenas através de /proc e ps , e nem todas as variantes unix possuem esse tipo de recurso, para que as aplicações não dependam disso.

No que diz respeito ao kernel, o ambiente aparece apenas como o argumento da execve chamada do sistema que inicia o programa. O Linux expõe uma área na memória através de /proc , e alguns programas atualizam esta área enquanto outros não. Em particular, não acho que qualquer shell atualize essa área. Como a área tem um tamanho fixo, seria impossível adicionar novas variáveis ou alterar o tamanho de um valor.

    
por 15.01.2012 / 03:49
37

Você pode ler o ambiente inicial de um processo em /proc/<pid>/environ .

Se um processo alterar seu ambiente, para ler o ambiente, você deve ter a tabela de símbolos do processo e usar a chamada de sistema ptrace (por exemplo, usando gdb ) ler o ambiente da variável global char **__environ . Não há outra maneira de obter o valor de qualquer variável de um processo Linux em execução.

Essa é a resposta. Agora, para algumas anotações.

O exemplo acima assume que o processo é compatível com POSIX, o que significa que o processo gerencia seu ambiente usando uma variável global char **__environ conforme especificado no Ref Spec .

O ambiente inicial de um processo é passado para o processo em um buffer de tamanho fixo na pilha do processo. (O mecanismo usual que faz isso é linux//fs/exec.c:do_execve_common(...) .) Como o tamanho do buffer é calculado para não ser maior do que o tamanho necessário para o ambiente inicial, não é possível adicionar novas variáveis sem apagar as variáveis existentes ou esmagá-las. Assim, qualquer esquema razoável para permitir mudanças no ambiente de um processo usaria o heap, onde a memória em tamanhos arbitrários pode ser alocada e liberada, o que é exatamente o que o GNU libc ( glibc ) faz por você.

Se o processo usar glibc , ele será compatível com POSIX, com __environ sendo declarado em glibc//posix/environ.c Glibc inicializando __environ com um ponteiro para a memória que malloc s do heap do processo e cópias o ambiente inicial da pilha para essa área de heap. Cada vez que o processo usa a função setenv , glibc faz um realloc para ajustar o tamanho da área que __environ aponta para acomodar o novo valor ou variável. (Você pode baixar o código-fonte glibc com git clone git://sourceware.org/git/glibc.git glibc ). Para realmente entender o mecanismo, você também terá que ler o código do Hurd em hurd//init/init.c:frob_kernel_process() (git clone git: //git.sv.gnu.org/hurd/hurd.git hurd).

Agora, se o novo processo for apenas fork ed, sem uma subsequente exec sobrescrevendo a pilha, a mágica de argumento e cópia do ambiente será feita em linux//kernel/fork.c:do_fork(...) , em que a rotina copy_process chama dup_task_struct que aloca a pilha do novo processo chamando alloc_thread_info_node , que chama setup_thread_stack ( linux//include/linux/sched.h ) para o novo processo usando alloc_thread_info_node .

Finalmente, a convenção POSIX __environ é uma convenção espaço de usuário . Não tem conexão com nada no kernel do Linux. Você pode escrever um programa de espaço do usuário sem usar glibc e sem o __environ global e gerenciar as variáveis de ambiente da forma que desejar. Ninguém vai prendê-lo por fazer isso, mas você terá que escrever suas próprias funções de gerenciamento de ambiente ( setenv / getenv ) e seus próprios wrappers para sys_exec e é provável que ninguém seja capaz de adivinhar onde você coloque as alterações no seu ambiente.

    
por 30.03.2013 / 20:44
18

Ele é atualizado à medida que o processo adquire / exclui suas variáveis de ambiente. Você tem uma referência que declara que o arquivo environ não está atualizado para o processo em seu diretório de processos sob o sistema de arquivos / proc?

xargs --null --max-args=1 echo < /proc/self/environ

ou

xargs --null --max-args=1 echo < /proc/<pid>/environ

ou

ps e -p <pid>

O texto acima irá imprimir as variáveis de ambiente do processo no formato ps , processamento de texto (análise / filtragem) é necessário para ver as variáveis de ambiente como uma lista.

Solaris (não solicitado, mas para referência eu vou postar aqui):

/usr/ucb/ps -wwwe <pid>

ou

pargs -e <pid> 

EDITAR: / proc / pid / environ não é atualizado! Eu estou corrigido. O processo de verificação está abaixo. No entanto, os filhos dos quais o processo é fork'd herdam a variável de ambiente de processo e ela é visível em seus respectivos arquivos / proc / self / environ. (Use strings)

Com o shell: aqui xargs é um processo filho e, portanto, herda a variável de ambiente e também reflete em seu arquivo /proc/self/environ .

[centos@centos t]$ printenv  | grep MASK
[centos@centos t]$ export MASK=NIKHIL
[centos@centos t]$ printenv  | grep MASK
MASK=NIKHIL
[centos@centos t]$ xargs --null --max-args=1 echo < /proc/self/environ  | grep MASK
MASK=NIKHIL
[centos@centos t]$ unset MASK
[centos@centos t]$ printenv  | grep MASK
[centos@centos t]$ xargs --null --max-args=1 echo < /proc/self/environ  | grep MASK
[centos@centos t]$

Verificando a partir de outra sessão, onde o terminal / sessão não é o processo filho do shell em que a variável de ambiente está configurada.

Verificando de outro terminal / sessão no mesmo host:

terminal1: : Observe que printenv é fork'd e é um processo filho de bash e, portanto, ele lê seu próprio arquivo environ.

[centos@centos t]$ echo $$
2610
[centos@centos t]$ export SPIDEY=NIKHIL
[centos@centos t]$ printenv | grep SPIDEY
SPIDEY=NIKHIL
[centos@centos t]$ 

terminal2: no mesmo host - não inicie com o mesmo shell onde a variável acima foi definida, inicie o terminal separadamente.

[centos@centos ~]$ echo $$
4436
[centos@centos ~]$ xargs --null --max-args=1 echo < /proc/self/environ | grep -i spidey
[centos@centos ~]$ strings -f /proc/2610/environ | grep -i spidey
[centos@centos ~]$ xargs --null --max-args=1 echo < /proc/2610/environ | grep -i spidey
[centos@centos ~]$ 
    
por 14.01.2012 / 18:08
7

Bem, o seguinte não está relacionado com as intenções reais do autor, mas se você realmente quiser "LER" o /proc/<pid>/environ , você pode tentar

strings /proc/<pid>/environ

que é melhor que cat it.

    
por 31.08.2017 / 09:13