Come leggere le variabili di ambiente di un processo


Risposte:


20

/proc/$pid/environsi aggiorna se il processo cambia il proprio ambiente. Ma molti programmi non si preoccupano di cambiare il proprio ambiente, perché è un po 'inutile: l'ambiente di un programma non è visibile attraverso i canali normali, solo attraverso /proce ps, e persino non tutte le varianti unix hanno questo tipo di funzionalità, quindi le applicazioni non fanno affidamento su di esso.

Per quanto riguarda il kernel, l'ambiente appare solo come argomento della execvechiamata di sistema che avvia il programma. Linux espone un'area in memoria /proc, e alcuni programmi aggiornano quest'area mentre altri no. In particolare, non credo che nessuna shell aggiorni quest'area. Poiché l'area ha una dimensione fissa, sarebbe impossibile aggiungere nuove variabili o modificare la lunghezza di un valore.


quindi, effettivamente non c'è modo di accedere al processo '* envp (array di puntatori alle impostazioni dell'ambiente). @Gilles, puoi mostrare se è possibile collegare il debugger e leggere l'array di puntatori alle impostazioni dell'ambiente.
Nikhil Mulley,

2
@Nikhil Certo che lo è. Ma solo perché scrivi PATH=fooin una shell non significa che la shell cambierà *envp. In alcune shell, questo ha aggiornato solo una struttura di dati interna ed è il codice di esecuzione del programma esterno che aggiorna *envp. Guarda assign_in_envdentro variables.cnella fonte bash, per esempio.
Gilles 'SO- smetti di essere malvagio' il

9
@Gilles: Questa risposta è nella migliore delle ipotesi fuorviante (-1). L'ambiente in / proc / $$ / environment viene letto dallo stack del processo. Vedi fs / proc / base.c. Questo è l'ambiente iniziale. Non viene mai aggiornato e in effetti non può essere. L'ambiente utilizzato da libc setenv viene allocato nell'heap e inizializzato con il contenuto dell'ambiente nello stack. Se il processo chiama libc, forkallora libc effettua la sys_forkchiamata usando l'ambiente allocato heap per il processo figlio.
Jonathan Ben-Avraham,

7
@ JonathanBen-Avraham Hai ragione sul fatto che l'ambiente iniziale non viene aggiornato in nessuna shell. Comunque quell'area non è letta solo sotto Linux, ho incontrato programmi che la usano per segnalare il loro stato (i rapporti sullo stato argvsono più comuni ma entrambi esistono).
Gilles 'SO- smetti di essere malvagio' il

39

È possibile leggere l' ambiente iniziale di un processo da /proc/<pid>/environ.

Se un processo cambia il suo ambiente, per poter leggere l'ambiente è necessario disporre della tabella dei simboli per il processo e utilizzare la ptracechiamata di sistema (ad esempio utilizzando gdb) per leggere l'ambiente dalla char **__environvariabile globale . Non esiste altro modo per ottenere il valore di qualsiasi variabile da un processo Linux in esecuzione.

Questa è la risposta Ora per alcune note.

Quanto sopra presuppone che il processo è conforme a POSIX, il che significa che il processo gestisce suo ambiente utilizzando una variabile globale char **__environcome specificato nel Rif Spec .

L'ambiente iniziale per un processo viene passato al processo in un buffer a lunghezza fissa nello stack del processo. (Il solito meccanismo che lo fa è linux//fs/exec.c:do_execve_common(...).) Poiché la dimensione del buffer non è superiore alla dimensione richiesta per l'ambiente iniziale, non è possibile aggiungere nuove variabili senza cancellare le variabili esistenti o distruggere lo stack. Quindi, qualsiasi schema ragionevole per consentire cambiamenti nell'ambiente di un processo userebbe l'heap, dove la memoria in dimensioni arbitrarie può essere allocata e liberata, che è esattamente ciò che GNU libc( glibc) fa per te.

Se il processo utilizza glibc, allora è conforme a POSIX, con l' __environessere dichiarato in glibc//posix/environ.cGlibc inizializzato __environcon un puntatore alla memoria che mallocproviene dall'heap del processo, quindi copia l'ambiente iniziale dallo stack in questa area dell'heap. Ogni volta che il processo utilizza la setenvfunzione, glibcesegue una reallocregolazione della dimensione dell'area a cui __environpunta per accogliere il nuovo valore o variabile. (Puoi scaricare il codice sorgente di glibc con git clone git://sourceware.org/git/glibc.git glibc). Per capire veramente il meccanismo dovrai anche leggere il codice Hurd in hurd//init/init.c:frob_kernel_process()(git clone git: //git.sv.gnu.org/hurd/hurd.git hurd).

Ora se il nuovo processo viene solo forkeditato, senza una successiva execsovrascrittura dello stack, viene eseguita l'argomento e la magia della copia dell'ambiente linux//kernel/fork.c:do_fork(...), in cui vengono copy_processchiamate le routine dup_task_structche allocano lo stack del nuovo processo chiamando alloc_thread_info_node, che chiama setup_thread_stack( linux//include/linux/sched.h) per il nuovo processo utilizzando alloc_thread_info_node.

Infine, la __environconvenzione POSIX è una convenzione spazio utente . Non ha alcuna connessione con nulla nel kernel Linux. Puoi scrivere un programma userspace senza usare glibce senza il __environglobale e quindi gestire le variabili d'ambiente come preferisci. Nessuno ti arresterà per farlo, ma dovrai scrivere le tue funzioni di gestione dell'ambiente ( setenv/ getenv) e i tuoi wrapper per sys_execed è probabile che nessuno sarà in grado di indovinare dove apporti le modifiche al tuo ambiente.


Molti dei file /proc/[pid]/sembrano avere una strana codifica (qualcun altro potrebbe sapere cosa e perché). Per me, semplicemente cat environstamperei le variabili di ambiente in un formato davvero difficile da leggere. cat environ | stringsrisolto questo per me.
retrohacker,

@retrohacker Questo offre una soluzione più solida: askubuntu.com/questions/978711/…
Frank Kusters,

20

Viene aggiornato come e quando il processo acquisisce / elimina le sue variabili di ambiente. Hai un riferimento che indica che il environfile non è aggiornato per il processo nella sua directory di processo nel filesystem / proc?

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

o

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

o

ps e -p <pid>

Quanto sopra stamperà le variabili di ambiente del processo nel psformato di output, l'elaborazione del testo (analisi / filtro) è necessaria per visualizzare le variabili di ambiente come un elenco.

Solaris (non richiesto, ma per riferimento posterò qui):

/usr/ucb/ps -wwwe <pid>

o

pargs -e <pid> 

EDIT: / proc / pid / environment non è aggiornato! Sono corretto. Il processo di verifica è di seguito. Tuttavia, i figli da cui viene eseguito il fork ereditano la variabile di ambiente di processo ed è visibile nel rispettivo file / proc / self / environment. (Usa le stringhe)

Con nella shell: qui xargs è un processo figlio e quindi eredita la variabile di ambiente e si riflette anche nel suo /proc/self/environfile.

[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]$

Controllandolo da un'altra sessione, in cui il terminale / sessione non è il processo figlio della shell in cui è impostata la variabile di ambiente.

Verifica da un altro terminale / sessione sullo stesso host:

terminal1:: Notare che printenv è fork ed è un processo figlio di bash e quindi legge il proprio file environment.

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

terminal2: sullo stesso host - non avviarlo con la stessa shell in cui è stata impostata la variabile sopra, avviare il terminale separatamente.

[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 ~]$ 

1
Faccio export foo=barnella sessione di one bash (pid xxxx), poi faccio cat /proc/xxxx/environ | tr \\0 \\nnella sessione di altri bash e non vedo foo.

Ho aggiornato la risposta sopra con un esempio che controlla lo stesso processo all'interno della shell.
Nikhil Mulley,

Hai ragione. Sono corretto. Grazie. Ora devo andare a leggere i miei manuali per verificare le variabili di ambiente di un processo diverso con nel gruppo di processi utente.
Nikhil Mulley,

1
Ancora una cosa: ho provato a controllare l'ambiente collegato gdb al pid, ma ancora nessun riferimento lì. Il blocco delle variabili di ambiente nella memoria viene riallocato ogni volta che si verifica una modifica e non si riflette nel file di ambiente del proprio processo nel filesystem proc, ma consente comunque di essere ereditato dal processo figlio. Ciò significa che potrebbe essere più semplice conoscere i dettagli intrinseci quando si verifica il fork, in che modo il processo figlio ottiene le variabili di ambiente così come sono.
Nikhil Mulley,

Spero che @Gilles accenda un po 'della sua torcia su questo .. :-)
Nikhil Mulley

7

Bene, quanto segue non è correlato alle reali intenzioni dell'autore, ma se vuoi davvero "LEGGERE" il /proc/<pid>/environ, puoi provare

strings /proc/<pid>/environ

che è meglio di catcosì.


1
+1 per strings. Mantienilo semplice.
Ed Randall,

Accetto @EdRandall, questo sembra l'approccio più semplice contro xargs --null.
Per Lundberg,

Il "file" termina con null, sostituisce i null con new-line e gli strumenti normali funzionano di nuovo (con i soliti avvertimenti), ad esempio:tr '\0' '\n' < /proc/$$/environ | ...
Thor

Finché la variabile d'ambiente stessa non contiene nuove righe. Conchiglie come BASH a volte lo fanno per le "funzioni esportate".
Anthony
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.