Jak czytać zmienne środowiskowe procesu

43

Linux /proc/<pid>/environnie aktualizuje się (jak rozumiem, plik zawiera początkowe środowisko procesu).

Jak mogę odczytać aktualne środowisko procesu ?

Jonathan Ben-Avraham
źródło

Odpowiedzi:

20

/proc/$pid/environaktualizuje się, jeśli proces zmieni własne środowisko. Ale wiele programów nie zawraca sobie głowy zmianą własnego środowiska, ponieważ jest to trochę bezcelowe: środowisko programu nie jest widoczne przez normalne kanały, tylko przez /proci ps, i nawet nie każdy wariant unixa ma taką funkcję, więc aplikacje nie polegają na tym.

Jeśli chodzi o jądro, środowisko pojawia się tylko jako argument execvewywołania systemowego uruchamiającego program. Linux udostępnia obszar pamięci /proc, a niektóre programy aktualizują ten obszar, a inne nie. W szczególności nie sądzę, aby jakakolwiek powłoka aktualizowała ten obszar. Ponieważ obszar ma stały rozmiar, dodanie nowych zmiennych lub zmiana długości wartości byłaby niemożliwa.

Gilles „SO- przestań być zły”
źródło
tak więc skutecznie nie ma sposobu na uzyskanie dostępu do procesu * * envp (tablica wskaźników do ustawień środowiska). @Gilles, czy możesz pokazać, czy możliwe jest dołączenie debugera i przeczytać tablicę wskaźników do ustawień środowiska.
Nikhil Mulley,
2
@Nikhil Pewnie, że tak. Ale to, że piszesz PATH=foow powłoce, nie oznacza, że ​​powłoka się zmieni *envp. W niektórych powłokach aktualizuje to tylko wewnętrzną strukturę danych i aktualizuje się zewnętrzny kod wykonania programu *envp. Spójrz na przykład assign_in_envw variables.cźródle bash.
Gilles „SO- przestań być zły”
9
@Gilles: Ta odpowiedź jest co najmniej myląca (-1). Środowisko w / proc / $$ / Environment jest odczytywane ze stosu procesu. Zobacz fs / proc / base.c. To jest początkowe środowisko. Nigdy nie jest aktualizowany i w rzeczywistości nie może być. Środowisko, którego używa libc setenv, jest przydzielane na stercie i inicjowane z zawartością środowiska na stosie. Jeśli proces wywoła libc, forkto libc wykonuje sys_forkpołączenie, używając środowiska przydzielonego do sterty dla procesu potomnego.
Jonathan Ben-Avraham
7
@ JonathanBen-Avraham Masz rację, że początkowe środowisko nie jest aktualizowane w żadnej powłoce. Jednak ten obszar nie jest czytany tylko pod Linuksem, napotkałem programy, które używają go do zgłaszania swojego statusu (raporty o statusie argvsą częstsze, ale oba istnieją).
Gilles „SO- przestań być zły”
39

Możesz odczytać początkowe środowisko procesu z /proc/<pid>/environ.

Jeśli proces zmienia swoje środowisko, to aby odczytać środowisko, musisz mieć tabelę symboli dla procesu i użyć ptracewywołania systemowego (na przykład za pomocą gdb), aby odczytać środowisko ze char **__environzmiennej globalnej . Nie ma innego sposobu uzyskania wartości dowolnej zmiennej z uruchomionego procesu Linux.

To jest odpowiedź. Teraz kilka notatek.

Powyższe zakłada, że ​​proces jest zgodny z POSIX, co oznacza, że ​​proces zarządza swoim środowiskiem przy użyciu zmiennej globalnej char **__environokreślonej w Spec Spec .

Początkowe środowisko dla procesu jest przekazywane do procesu w buforze o stałej długości na stosie procesu. (Zwykłym mechanizmem, który to robi linux//fs/exec.c:do_execve_common(...).) Ponieważ rozmiar bufora jest obliczany jako nie większy niż rozmiar wymagany dla środowiska początkowego, nie można dodawać nowych zmiennych bez usuwania istniejących zmiennych lub niszczenia stosu. Tak więc każdy rozsądny schemat pozwalający na zmiany w środowisku procesu wykorzystywałby stertę, w której pamięć o dowolnych rozmiarach może być przydzielana i zwalniana, co dokładnie robi dla ciebie GNU libc( glibc).

Jeśli proces używa glibc, to jest zgodny z POSIX, a __environdeklaracja w glibc//posix/environ.cGlibc inicjuje __environwskaźnikiem do pamięci, że mallocpochodzi ze sterty procesu, a następnie kopiuje środowisko początkowe ze stosu do tego obszaru sterty. Za każdym razem proces wykorzystuje setenvfunkcję, glibcwymaga się realloc, aby dostosować rozmiar obszaru, który __environwskazuje, aby pomieścić nową wartość lub zmienną. (Możesz pobrać kod źródłowy glibc za pomocą git clone git://sourceware.org/git/glibc.git glibc). Aby naprawdę zrozumieć mechanizm, musisz także przeczytać kod Hurd w hurd//init/init.c:frob_kernel_process()(git clone git: //git.sv.gnu.org/hurd/hurd.git hurd).

Teraz, jeśli nowy proces jest tylko forkedytowany, bez późniejszego execnadpisywania stosu, to magia kopiowania argumentów i środowiska jest wykonywana linux//kernel/fork.c:do_fork(...), gdzie copy_processrutynowe wywołania, dup_task_structktóre przydzielają stos nowego procesu przez wywołanie alloc_thread_info_node, które wywołuje setup_thread_stack( linux//include/linux/sched.h) dla nowego procesu za pomocą alloc_thread_info_node.

Wreszcie __environkonwencja POSIX to konwencja przestrzeni użytkownika . Nie ma połączenia z niczym w jądrze Linuksa. Możesz napisać program przestrzeni użytkownika bez użycia globalnego i glibcbez niego, __environa następnie zarządzać zmiennymi środowiskowymi w dowolny sposób. Nikt cię nie aresztuje za to, ale będziesz musiał napisać własne funkcje zarządzania środowiskiem ( setenv/ getenv) i własne opakowania sys_execi jest prawdopodobne, że nikt nie będzie w stanie zgadnąć, gdzie wprowadzasz zmiany w swoim środowisku.

Jonathan Ben-Avraham
źródło
Wiele plików /proc/[pid]/wydaje się mieć dziwne kodowanie (ktoś inny może wiedzieć, co i dlaczego). Dla mnie po prostu cat environwydrukowałbym zmienne środowiskowe w naprawdę trudnym do odczytania formacie. cat environ | stringsrozwiązałem to dla mnie.
retrohacker
@retrohacker Daje to bardziej niezawodne rozwiązanie: askubuntu.com/questions/978711/…
Frank Kusters
20

Jest aktualizowany w miarę, jak proces nabywa / usuwa swoje zmienne środowiskowe. Czy masz odniesienie, które stwierdza, że environplik nie jest aktualizowany dla procesu w katalogu procesu w systemie plików / proc?

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

lub

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

lub

ps e -p <pid>

Powyżej wydrukuje zmienne środowiskowe procesu w psformacie wyjściowym, przetwarzanie tekstu (parsowanie / filtrowanie) jest wymagane, aby zobaczyć zmienne środowiskowe jako listę.

Solaris (nie pytany, ale w celach informacyjnych opublikuję tutaj):

/usr/ucb/ps -wwwe <pid>

lub

pargs -e <pid> 

EDYCJA: / proc / pid / Environment nie jest aktualizowany! Poprawiono mnie. Proces weryfikacji jest poniżej. Jednak elementy potomne, z których rozwidla się proces, dziedziczą zmienną środowiskową procesu i jest ona widoczna w odpowiednim pliku / proc / self / environment. (Użyj ciągów)

With in the shell: tutaj xargs jest procesem potomnym i dlatego dziedziczy zmienną środowiskową, a także odzwierciedla w swoim /proc/self/environpliku.

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

Sprawdzanie go z innej sesji, w której terminal / sesja nie jest procesem potomnym powłoki, w której ustawiona jest zmienna środowiskowa.

Weryfikacja z innego terminala / sesji na tym samym hoście:

terminal1: Zauważ, że printenv jest rozwidlony i jest potomnym procesem bash, a zatem czyta własny plik środowiska.

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

terminal2: na tym samym hoście - nie uruchamiaj go w tej samej powłoce, w której ustawiono powyższą zmienną, uruchom terminal osobno.

[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
źródło
1
Robię export foo=barw sesji jednego basha (pid xxxx), potem robię cat /proc/xxxx/environ | tr \\0 \\nw sesji innego basha i nie widzę foo.
Zaktualizowałem powyższą odpowiedź za pomocą przykładu sprawdzającego ten sam proces w powłoce.
Nikhil Mulley
Masz rację. Poprawiono mnie. Dzięki. Teraz muszę iść i przeczytać instrukcje, aby sprawdzić zmienne środowiskowe innego procesu w grupie procesów użytkownika.
Nikhil Mulley
1
Jeszcze jedno: próbowałem sprawdzić środowisko dołączające gdb do pid, ale wciąż nie ma tam odniesienia. Blok zmiennych środowiskowych w pamięci zostaje ponownie przydzielony za każdym razem, gdy zachodzi zmiana i nie odzwierciedla pliku środowiska własnego procesu w systemie plików proc, ale pozwala na dziedziczenie przez proces potomny. Oznacza to, że łatwiej będzie poznać wewnętrzne szczegóły, kiedy nastąpi rozwidlenie, w jaki sposób proces potomny pobiera zmienne środowiskowe w takiej postaci, w jakiej są.
Nikhil Mulley
Mam nadzieję, że @Gilles rzuci na to trochę swojej pochodni .. :-)
Nikhil Mulley
7

Cóż, poniższe informacje nie są powiązane z prawdziwymi intencjami autora, ale jeśli naprawdę chcesz „CZYTAĆ” /proc/<pid>/environ, możesz spróbować

strings /proc/<pid>/environ

co jest lepsze niż catto.

fibonacci
źródło
1
+1 dla strings. Nie komplikuj.
Ed Randall
Zgadzam się @EdRandall, wydaje się, że jest to łatwiejsze podejście niż xargs --null.
Per Lundberg,
„Plik” jest zakończony zerem, zastąp null znakiem nowej linii, a normalne narzędzia znów działają (ze zwykłymi zastrzeżeniami), np .:tr '\0' '\n' < /proc/$$/environ | ...
Thor
Dopóki sama zmienna środowiskowa nie zawiera znaków nowej linii. Powłoki takie jak BASH czasami robią to dla „eksportowanych funkcji”.
Anthony