So lesen Sie Umgebungsvariablen eines Prozesses


43

Linux /proc/<pid>/environwird nicht aktualisiert (soweit ich weiß, enthält die Datei die ursprüngliche Umgebung des Prozesses).

Wie kann ich die aktuelle Umgebung eines Prozesses lesen ?

Antworten:


20

/proc/$pid/environwird aktualisiert, wenn der Prozess seine eigene Umgebung ändert. Viele Programme machen sich jedoch nicht die Mühe, ihre eigene Umgebung zu ändern, da dies etwas sinnlos ist: Die Umgebung eines Programms ist nicht über normale Kanäle sichtbar, sondern nur über /procund ps, und nicht jede Unix-Variante verfügt über diese Funktion, sodass Anwendungen nicht darauf angewiesen sind darauf.

Für den Kernel erscheint die Umgebung nur als Argument des execveSystemaufrufs, der das Programm startet. Linux macht einen Bereich im Arbeitsspeicher durch verfügbar /proc, und einige Programme aktualisieren diesen Bereich, während andere dies nicht tun. Insbesondere glaube ich nicht, dass eine Shell diesen Bereich aktualisiert. Da der Bereich eine feste Größe hat, ist es unmöglich, neue Variablen hinzuzufügen oder die Länge eines Werts zu ändern.


Daher gibt es effektiv keine Möglichkeit, auf den Prozess '* envp (Array von Zeigern auf Umgebungseinstellungen) zuzugreifen. @Gilles, können Sie bitte zeigen, ob es möglich ist, den Debugger anzuhängen und das Array von Zeigern auf Umgebungseinstellungen zu lesen.
Nikhil Mulley

2
@ Nikhil Sicher ist es. Nur weil Sie PATH=fooin eine Shell schreiben, heißt das noch lange nicht, dass die Shell geändert wird *envp. In einigen Shells wurde nur eine interne Datenstruktur aktualisiert, und es wird der externe Programmausführungscode aktualisiert *envp. Schauen Sie sich assign_in_envin variables.cin der Bash - Quelle, zum Beispiel.
Gilles 'SO- hör auf böse zu sein'

9
@ Gilles: Diese Antwort ist bestenfalls irreführend (-1). Die Umgebung in / proc / $$ / environ wird vom Stapel des Prozesses gelesen. Siehe fs / proc / base.c. Dies ist die ursprüngliche Umgebung. Es wird nie aktualisiert und kann es auch nicht sein. Die von libc setenv verwendete Umgebung wird auf dem Heap zugeordnet und mit dem Inhalt der Umgebung im Stack initialisiert. Wenn der Prozess libc aufruft, führt libc forkden sys_forkAufruf unter Verwendung der Heap-zugewiesenen Umgebung für den untergeordneten Prozess durch.
Jonathan Ben-Avraham

7
@ JonathanBen-Avraham Sie haben Recht, dass die ursprüngliche Umgebung in keiner Shell aktualisiert wird. Dieser Bereich ist jedoch nicht nur unter Linux lesbar. Ich habe Programme gefunden, die ihn zur Statusmeldung verwenden (Statusmeldungen über argvsind häufiger, aber beide sind vorhanden).
Gilles 'SO- hör auf böse zu sein'

39

Sie können die ursprüngliche Umgebung eines Prozesses aus lesen /proc/<pid>/environ.

Wenn ein Prozess seine Umgebung ändert , müssen Sie zum Lesen der Umgebung über die Symboltabelle für den Prozess verfügen und den ptraceSystemaufruf (z. B. mithilfe von gdb) verwenden, um die Umgebung aus der globalen char **__environVariablen zu lesen . Es gibt keine andere Möglichkeit, den Wert einer Variablen aus einem laufenden Linux-Prozess abzurufen.

Das ist die Antwort. Nun zu einigen Notizen.

Bei den obigen Ausführungen wird davon ausgegangen, dass der Prozess POSIX-kompatibel ist. Dies bedeutet, dass der Prozess seine Umgebung mithilfe einer globalen Variablen verwaltet char **__environ, die in der Referenzspezifikation angegeben ist .

Die ursprüngliche Umgebung für einen Prozess wird in einem Puffer fester Länge auf dem Prozessstapel an den Prozess übergeben. (Der übliche Mechanismus hierfür ist der folgende linux//fs/exec.c:do_execve_common(...).) Da die Größe des Puffers so berechnet wird, dass sie nicht größer ist als die für die ursprüngliche Umgebung erforderliche Größe, können Sie keine neuen Variablen hinzufügen, ohne vorhandene Variablen zu löschen oder den Stapel zu zerschlagen. Jedes vernünftige Schema, um Änderungen in der Umgebung eines Prozesses zuzulassen, würde den Heap verwenden, in dem Speicher in beliebigen Größen zugewiesen und freigegeben werden kann. Genau das macht GNU libc( glibc) für Sie.

Wenn der Prozess verwendet glibc, ist er POSIX-kompatibel. Wenn __environer in glibc//posix/environ.cGlibc deklariert wird, wird er __environmit einem Zeiger auf den Speicher initialisiert , der mallocaus dem Heap des Prozesses stammt. Anschließend wird die ursprüngliche Umgebung vom Stapel in diesen Heapbereich kopiert. Jedes Mal, wenn der Prozess die setenvFunktion verwendet, glibcwird a ausgeführt realloc, um die Größe des Bereichs anzupassen, auf den __environder neue Wert oder die neue Variable verweist. (Sie können den glibc-Quellcode mit herunterladen git clone git://sourceware.org/git/glibc.git glibc). Um den Mechanismus wirklich zu verstehen, müssen Sie auch den Hurd-Code einlesen hurd//init/init.c:frob_kernel_process()(git clone git: //git.sv.gnu.org/hurd/hurd.git hurd).

Wenn der neue Prozess nur forkbearbeitet wird, ohne execden Stapel anschließend zu überschreiben, wird das Argument und die Umgebung kopiert. Dabei ruft linux//kernel/fork.c:do_fork(...)die copy_processRoutine auf dup_task_struct, die den Stapel des neuen Prozesses durch Aufrufen von alloc_thread_info_nodeaufruft setup_thread_stack( linux//include/linux/sched.h) für den neuen Prozess mit alloc_thread_info_node.

Schließlich ist die POSIX- __environKonvention eine User-Space- Konvention. Es hat keine Verbindung mit irgendetwas im Linux-Kernel. Sie können ein Userspace-Programm ohne glibcund ohne das __environGlobale schreiben und dann die Umgebungsvariablen verwalten, wie Sie möchten. Niemand wird Sie dafür verhaften, aber Sie müssen Ihre eigenen Umgebungsverwaltungsfunktionen ( setenv/ getenv) und Ihre eigenen Wrapper dafür schreiben, sys_execund es ist wahrscheinlich, dass niemand ahnen kann, wo Sie die Änderungen an Ihrer Umgebung vorgenommen haben.


Viele der Dateien in /proc/[pid]/scheinen eine seltsame Kodierung zu haben (jemand anderes weiß möglicherweise, was und warum). Für mich cat environwürde man einfach die Umgebungsvariablen in einem wirklich schwer lesbaren Format ausdrucken. cat environ | stringslöste das für mich.
Retrohacker

@ Retrohacker Dies ergibt eine robustere Lösung: askubuntu.com/questions/978711/…
Frank Kusters

20

Es wird aktualisiert, wenn der Prozess seine Umgebungsvariablen erfasst / löscht. Haben Sie eine Referenz, die besagt, dass die environDatei für den Prozess in ihrem Prozessverzeichnis unter / proc Dateisystem nicht aktualisiert wurde?

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

oder

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

oder

ps e -p <pid>

Mit dem obigen Befehl werden die Umgebungsvariablen des Prozesses im psAusgabeformat gedruckt. Eine Textverarbeitung (Parsen / Filtern) ist erforderlich, um die Umgebungsvariablen als Liste anzuzeigen.

Solaris (nicht gefragt, aber als Referenz werde ich hier posten):

/usr/ucb/ps -wwwe <pid>

oder

pargs -e <pid> 

EDIT: / proc / pid / environ wird nicht aktualisiert! Ich stehe korrigiert. Überprüfungsprozess ist unten. Die untergeordneten Elemente, von denen der Prozess abgeleitet wurde, erben jedoch die Prozessumgebungsvariable und diese wird in der jeweiligen Datei / proc / self / environ angezeigt. (Benutze Strings)

Mit in der Shell: Hier ist xargs ein untergeordneter Prozess und erbt daher die Umgebungsvariable und spiegelt sich auch in seiner /proc/self/environDatei wider .

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

Überprüfung aus einer anderen Sitzung, in der das Terminal / die Sitzung nicht der untergeordnete Prozess der Shell ist, in der die Umgebungsvariable festgelegt ist.

Überprüfung von einem anderen Terminal / einer anderen Sitzung auf demselben Host:

terminal1 :: Beachten Sie, dass printenv fork'd und ein untergeordneter Prozess von bash ist und daher seine eigene Umgebungsdatei liest.

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

terminal2: auf demselben Host - starten Sie es nicht in derselben Shell, in der die obige Variable festgelegt wurde, sondern starten Sie das Terminal separat.

[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
Ich mache export foo=barin der einen Bash-Sitzung (pid xxxx), dann mache ich cat /proc/xxxx/environ | tr \\0 \\nin der anderen Bash-Sitzung und ich sehe nicht foo.

Ich habe die obige Antwort mit einem Beispiel aktualisiert, das denselben Prozess in der Shell überprüft.
Nikhil Mulley

Du hast Recht. Ich stehe korrigiert. Vielen Dank. Ich muss jetzt meine Handbücher lesen, um die Umgebungsvariablen eines anderen Prozesses in der Benutzerprozessgruppe zu überprüfen.
Nikhil Mulley

1
Eine weitere Sache: Ich habe versucht, die Umgebung zu überprüfen, die gdb an die PID angehängt ist, aber immer noch keinen Hinweis darauf. Der Block der Umgebungsvariablen im Speicher wird bei jeder Änderung neu zugeordnet und spiegelt sich nicht in der Umgebungsdatei des eigenen Prozesses im proc-Dateisystem wider, kann jedoch vom untergeordneten Prozess geerbt werden. Dies bedeutet, dass es einfacher sein kann, die eigentlichen Details zu kennen, wenn die Verzweigung auftritt und wie der untergeordnete Prozess Umgebungsvariablen unverändert kopiert.
Nikhil Mulley

Ich hoffe, @Gilles würde etwas von seiner Taschenlampe auf diese werfen .. :-)
Nikhil Mulley

7

Nun, das Folgende hängt nicht mit den wahren Absichten des Autors zusammen, aber wenn Sie das wirklich "LESEN" wollen /proc/<pid>/environ, können Sie es versuchen

strings /proc/<pid>/environ

was ist besser als cates.


1
+1 für strings. Halte es einfach.
Ed Randall

Stimmen Sie @EdRandall zu, das fühlt sich an wie der einfachere Ansatz gegenüber xargs --null.
Per Lundberg

Die "Datei" ist nullterminiert, Nullen durch New-Line ersetzen und normale Tools funktionieren wieder (mit den üblichen Einschränkungen), zB:tr '\0' '\n' < /proc/$$/environ | ...
Thor
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.