Comment lire les variables d'environnement d'un processus

43

Linux /proc/<pid>/environne met pas à jour (si j'ai bien compris, le fichier contient l'environnement initial du processus).

Comment lire l' environnement actuel d' un processus ?

Jonathan Ben-Avraham
la source

Réponses:

20

/proc/$pid/environmet à jour si le processus change son propre environnement. Mais de nombreux programmes ne prennent pas la peine de changer leur propre environnement, car il est un peu inutile: l'environnement d'un programme ne sont pas visibles par les voies normales, que par /procet ps, et même pas toutes les variantes unix a ce genre de fonction, afin que les applications ne reposent pas dessus.

En ce qui concerne le noyau, l'environnement n'apparaît que comme l'argument de l' execveappel système qui lance le programme. Linux expose une zone en mémoire /proc, et certains programmes la mettent à jour alors que d’autres ne le font pas. En particulier, je ne pense pas qu’un shell mette à jour ce domaine. Comme la zone a une taille fixe, il serait impossible d’ajouter de nouvelles variables ou de modifier la longueur d’une valeur.

Gilles, arrête de faire le mal
la source
alors, en réalité, il n’ya aucun moyen d’accéder au processus '* envp (tableau de pointeurs vers les paramètres d’environnement). @ Gilles, pouvez-vous s'il vous plaît montrer s'il est possible d'attacher le débogueur et de lire le tableau de pointeurs vers les paramètres de l'environnement.
Nikhil Mulley
2
@ Nikhil Bien sûr, ça l'est. Mais ce n'est pas parce que vous écrivez PATH=foodans un shell que celui-ci va être modifié *envp. Dans certains shells, seule la structure de données interne est mise à jour et le code d'exécution du programme externe est mis à jour *envp. Regardez assign_in_envdans variables.cla source de bash, par exemple.
Gilles 'SO- arrête d'être méchant'
9
@ Gilles: Cette réponse est au mieux trompeuse (-1). L'environnement dans / proc / $$ / environ est lu à partir de la pile du processus. Voir fs / proc / base.c. Ceci est l'environnement initial. Il n'est jamais mis à jour et ne peut en fait l'être. L'environnement utilisé par libc setenv est alloué sur le tas et initialisé avec le contenu de l'environnement dans la pile. Si le processus appelle libc, forkalors libc effectue l' sys_forkappel à l'aide de l'environnement alloué par tas pour le processus enfant.
Jonathan Ben-Avraham
7
@ JonathanBen-Avraham Vous avez raison de dire que l'environnement initial n'est mis à jour dans aucun shell. Cependant, cette zone n’est pas lue uniquement sous Linux, j’ai rencontré des programmes qui l’utilisent pour signaler leur statut (les rapports d’état argvsont plus courants mais les deux existent).
Gilles 'SO- arrête d'être méchant'
39

Vous pouvez lire l' environnement initial d'un processus à partir de /proc/<pid>/environ.

Si un processus modifie son environnement, pour lire l'environnement, vous devez disposer de la table des symboles pour le processus et utiliser l' ptraceappel système (par exemple, à l'aide de gdb) pour lire l'environnement à partir de la char **__environvariable globale . Il n'y a pas d'autre moyen d'obtenir la valeur d'une variable à partir d'un processus Linux en cours d'exécution.

C'est la réponse. Maintenant pour quelques notes.

Ce qui précède suppose que le processus est conforme à POSIX, ce qui signifie qu'il gère son environnement à l'aide d'une variable globale char **__environspécifiée dans la spécification de référence .

L'environnement initial d'un processus est transmis au processus dans un tampon de longueur fixe sur la pile du processus. (Le mécanisme habituel est le cas linux//fs/exec.c:do_execve_common(...).) Étant donné que la taille de la mémoire tampon est calculée au maximum de la taille requise pour l'environnement initial, vous ne pouvez pas ajouter de nouvelles variables sans effacer les variables existantes ou détruire la pile. Ainsi, tout schéma raisonnable permettant des modifications dans l'environnement d'un processus utiliserait le tas, où de la mémoire de tailles arbitraires peut être allouée et libérée, ce qui est exactement ce que GNU libc( glibc) fait pour vous.

Si le processus utilise glibc, il est conforme à POSIX. Après __environavoir été déclaré dans glibc//posix/environ.cGlibc, il est initialisé __environavec un pointeur sur la mémoire mallocextraite du segment de mémoire du processus, puis copie l’environnement initial de la pile dans cette zone. Chaque fois que le processus utilise la setenvfonction, glibcne a reallocpour ajuster la taille de la surface qui __environpointe vers pour tenir compte de la nouvelle valeur ou variable. (Vous pouvez télécharger le code source de glibc avec git clone git://sourceware.org/git/glibc.git glibc). Pour bien comprendre le mécanisme, vous devrez également lire le code Hurd dans hurd//init/init.c:frob_kernel_process()(git clone git: //git.sv.gnu.org/hurd/hurd.git hurd).

Maintenant , si le nouveau processus forkn'ed, sans suite execécraser la pile, l'argument et l' environnement magique de la copie se fait dans linux//kernel/fork.c:do_fork(...), où les copy_processappels de routine dup_task_structqui attribue la pile du nouveau processus en appelant alloc_thread_info_node, qui appelle setup_thread_stack( linux//include/linux/sched.h) pour le nouveau processus à l' aide alloc_thread_info_node.

Enfin, la __environconvention POSIX est une convention d’ espace utilisateur . Il n’a aucun lien avec quoi que ce soit dans le noyau Linux. Vous pouvez écrire un programme en espace utilisateur sans utiliser glibcet sans le __environglobal, puis gérer les variables d'environnement comme bon vous semble. Personne ne vous arrêtera pour cela, mais vous devrez écrire vos propres fonctions de gestion de l’environnement ( setenv/ getenv) et vos propres wrappers sys_exec. Il est probable que personne ne sera en mesure de deviner où vous mettez les changements dans votre environnement.

Jonathan Ben-Avraham
la source
De nombreux fichiers /proc/[pid]/semblent avoir un encodage étrange (quelqu'un d'autre peut savoir quoi et pourquoi). Pour moi, il cat environsuffirait d’imprimer les variables d’environnement dans un format très difficile à lire. cat environ | stringsrésolu ceci pour moi.
retrohacker
@retrohacker Cela donne une solution plus robuste: askubuntu.com/questions/978711/…
Frank Kusters
20

Il est mis à jour au fur et à mesure que le processus acquiert / supprime ses variables d'environnement. Avez-vous une référence indiquant que le environfichier n'est pas mis à jour pour le processus dans son répertoire de processus sous / proc filesystem?

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

ou

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

ou

ps e -p <pid>

Ce qui précède imprimera les variables d’environnement du processus au psformat de sortie. Un traitement de texte (analyse / filtrage) est nécessaire pour afficher les variables d’environnement sous forme de liste.

Solaris (non demandé, mais pour référence je posterai ici):

/usr/ucb/ps -wwwe <pid>

ou

pargs -e <pid> 

EDIT: / proc / pid / environ n'est pas mis à jour! Je me suis trompé. Le processus de vérification est ci-dessous. Cependant, les enfants dont le processus est fork héritent de la variable d'environnement de processus et celle-ci est visible dans leur fichier / proc / self / environ respectif. (Utilisez des chaînes)

Avec dans le shell: xargs est ici un processus enfant et hérite donc de la variable d’environnement et se reflète également dans son /proc/self/environfichier.

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

Vérification à partir d'une autre session, où le terminal / session n'est pas le processus enfant du shell où la variable d'environnement est définie.

Vérification à partir d'un autre terminal / session sur le même hôte:

terminal1:: Notez que printenv est fork'd et est un processus enfant de bash et qu'il lit donc son propre fichier environ.

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

terminal2: sur le même hôte - ne le lancez pas dans le même shell que celui où la variable ci-dessus a été définie, lancez le terminal séparément.

[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 ~]$ 
Nikhil Mulley
la source
1
Je fais export foo=bardans la session de one bash (pid xxxx), puis cat /proc/xxxx/environ | tr \\0 \\ndans la session de l'autre bash et je ne vois pas foo.
J'ai mis à jour la réponse ci-dessus avec un exemple vérifiant le même processus dans le shell.
Nikhil Mulley
Vous avez raison. Je me suis trompé. Merci. Je dois maintenant aller lire mes manuels pour vérifier les variables d'environnement d'un processus différent dans le groupe de processus utilisateur.
Nikhil Mulley
1
Une dernière chose: j’ai essayé de vérifier l’environnement en joignant le gdb au pid, mais toujours aucune référence. Le bloc de variables d'environnement en mémoire est réaffecté chaque fois qu'il y a un changement et qu'il n'est pas reflété dans le fichier d'environnement de son propre processus dans le système de fichiers proc, mais permet cependant d'être hérité par le processus enfant. Cela signifie que cela pourrait devenir plus facile de connaître les détails intrinsèques lorsque la fourchette se produit, comment le processus enfant obtient-il les variables d'environnement copiées telles quelles.
Nikhil Mulley
J'espère que @Gilles jetterait un peu de son flambeau à ce sujet .. :-)
Nikhil Mulley
7

Ce qui suit n’est pas lié aux intentions réelles de l’auteur, mais si vous voulez vraiment "LIRE" le /proc/<pid>/environ, vous pouvez essayer

strings /proc/<pid>/environ

qui est meilleur que catça.

fibonacci
la source
1
+1 pour strings. Rester simple.
Ed Randall
D' accord @EdRandall, cela se sent comme l'approche plus facile vs xargs --null.
Par Lundberg
Le "fichier" est terminé, remplacez les valeurs nulles par une nouvelle ligne et les outils normaux fonctionnent à nouveau (avec les mises en garde habituelles), par exemple:tr '\0' '\n' < /proc/$$/environ | ...
Thor
Tant que la variable d'environnement elle-même ne contient pas de nouvelles lignes. Des coquilles comme BASH le font parfois pour des "fonctions exportées".
Anthony