web-dev-qa-db-fra.com

Comment lire les variables d'environnement d'un processus

/proc/<pid>/environ De Linux ne se met pas à jour (si je comprends bien, le fichier contient l'environnement initial du processus).

Comment lire l'environnement actuel d'un processus?

45
user14284

/proc/$pid/environ est mis à jour si le processus change son propre environnement. Mais de nombreux programmes ne prennent pas la peine de changer leur propre environnement, car c'est un peu inutile: l'environnement d'un programme n'est pas visible par les canaux normaux, seulement par /proc et ps, et même toutes les variantes Unix n'ont pas ce genre de fonctionnalité, donc les applications ne comptent pas dessus.

En ce qui concerne le noyau, l'environnement n'apparaît que comme l'argument de l'appel système execve qui démarre le programme. Linux expose une zone en mémoire via /proc, et certains programmes mettent à jour cette zone tandis que d'autres ne le font pas. En particulier, je ne pense pas que Shell mette à jour cette zone. La zone ayant une taille fixe, il serait impossible d'ajouter de nouvelles variables ou de modifier la longueur d'une valeur.

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

Si un processus change son environnement, alors pour lire l'environnement, vous devez avoir la table des symboles du processus et utiliser le ptrace appel système (par exemple en utilisant gdb) pour lire l'environnement à partir de la variable globale char **__environ. Il n'y a aucun autre moyen d'obtenir la valeur d'une variable à partir d'un processus Linux en cours d'exécution.

Voilà la réponse. Maintenant, pour quelques notes.

Ce qui précède suppose que le processus est conforme à POSIX, ce qui signifie que le processus gère son environnement à l'aide d'une variable globale char **__environ Comme spécifié dans Ref Spec .

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 qui le fait est linux//fs/exec.c:do_execve_common(...).) Étant donné que la taille du tampon n'est pas supérieure à la taille requise pour l'environnement initial, vous ne pouvez pas ajouter de nouvelles variables sans effacer les variables existantes ou brisant la pile. Ainsi, tout schéma raisonnable pour autoriser des modifications dans l'environnement d'un processus utiliserait le tas, où la mémoire de tailles arbitraires peut être allouée et libérée, ce qui est exactement ce qui GNU libc (glibc) fait pour vous.

Si le processus utilise glibc, il est conforme à POSIX, avec __environ Déclaré dans glibc//posix/environ.c Glibc initialise __environ Avec un pointeur sur la mémoire qu'il mallocs du tas du processus, puis copie l'environnement initial de la pile dans cette zone de tas. Chaque fois que le processus utilise la fonction setenv, glibc fait un realloc pour ajuster la taille de la zone vers laquelle __environ Pointe pour tenir compte de la nouvelle valeur ou variable. (Vous pouvez télécharger le code source de la glibc avec git clone git://sourceware.org/git/glibc.git glibc). Pour vraiment 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 est uniquement forked, sans qu'un exec ultérieur écrase la pile, alors la magie de copie d'argument et d'environnement se fait dans linux//kernel/fork.c:do_fork(...), où le copy_process Routine appelle dup_task_struct Qui alloue 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 en utilisant alloc_thread_info_node.

Enfin, la convention POSIX __environ Est une convention user-space. Il n'a aucun lien avec quoi que ce soit dans le noyau Linux. Vous pouvez écrire un programme d'espace utilisateur sans utiliser glibc et sans __environ Global, puis gérer les variables d'environnement comme vous le souhaitez. Personne ne vous arrêtera pour cela, mais vous devrez écrire vos propres fonctions de gestion d'environnement (setenv/getenv) et vos propres wrappers pour sys_exec Et il est probable que personne ne pourra deviner où vous placez les changements dans votre environnement.

41

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 fichier environ n'est pas mis à jour pour le processus dans son répertoire de processus sous le système de fichiers/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>

Ce qui précède imprimera les variables d'environnement du processus au format de sortie ps, le traitement de texte (analyse/filtrage) est nécessaire pour voir les variables d'environnement sous forme de liste.

Solaris (pas 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 elle est visible dans leur fichier respectif/proc/self/environ. (Utilisez des chaînes)

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

[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é 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 avec dans le même shell 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 ~]$ 
22
Nikhil Mulley

Eh bien, 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

ce qui est mieux que cat it.

8
fibonacci