Как читать переменные среды процесса

43

Linux /proc/<pid>/environне обновляется (насколько я понимаю, файл содержит исходную среду процесса).

Как я могу прочитать текущую среду процесса ?

Джонатан Бен-Авраам
источник

Ответы:

20

/proc/$pid/environобновляется, если процесс изменяет свою собственную среду. Но многие программы не беспокоятся об изменении собственной среды, потому что это немного бессмысленно: среда программы не видна через обычные каналы, только через /procи ps, и даже не в каждом варианте Unix есть такая функция, поэтому приложения не полагаются в теме.

Что касается ядра, окружение появляется только в качестве аргумента execveсистемного вызова, запускающего программу. Linux раскрывает область в памяти /proc, и некоторые программы обновляют эту область, а другие - нет. В частности, я не думаю, что какая-либо оболочка обновляет эту область. Поскольку область имеет фиксированный размер, было бы невозможно добавить новые переменные или изменить длину значения.

Жиль "ТАК - перестань быть злым"
источник
таким образом, фактически нет способа получить доступ к процессу * envp (массив указателей на параметры среды). @ Жиль, не могли бы вы показать, можно ли подключить отладчик и прочитать массив указателей на параметры среды.
Nikhil Mulley
2
@Nikhil Конечно, это так. Но то, что вы пишете PATH=fooв оболочке, не означает, что оболочка будет изменена *envp. В некоторых оболочках обновляется только внутренняя структура данных, и обновляется код выполнения внешней программы *envp. Посмотрите assign_in_envв variables.cв источнике Баш, например.
Жиль "ТАК - перестань быть злым"
9
@ Жиль: Этот ответ в лучшем случае вводит в заблуждение (-1). Среда в / proc / $$ / environment читается из стека процесса. Смотрите fs / proc / base.c. Это начальная среда. Это никогда не обновляется и фактически не может быть. Среда, которую использует libc setenv, размещается в куче и инициализируется с содержимым среды в стеке. Если процесс вызывает libc, forkто libc выполняет sys_forkвызов, используя выделенную кучу среду для дочернего процесса.
Джонатан Бен-Авраам
7
@ JonathanBen-Avraham Вы правы, что исходная среда не обновляется ни в одной оболочке. Однако эта область не только для чтения под Linux, я сталкивался с программами, которые используют ее, чтобы сообщить о своем состоянии (отчеты о состоянии через argvболее распространены, но оба существуют).
Жиль "ТАК - перестань быть злым"
39

Вы можете прочитать исходную среду процесса из /proc/<pid>/environ.

Если процесс изменяет свою среду, то для чтения среды необходимо иметь таблицу символов для процесса и использовать ptraceсистемный вызов (например, с помощью gdb) для чтения среды из глобальной char **__environпеременной. Нет другого способа получить значение любой переменной из запущенного процесса Linux.

Это ответ. Теперь о некоторых заметках.

Выше предполагается, что процесс совместим с POSIX, что означает, что процесс управляет своей средой, используя глобальную переменную, char **__environкак указано в Ссылочной спецификации .

Начальная среда для процесса передается процессу в буфере фиксированной длины в стеке процесса. (Обычный механизм, который делает это linux//fs/exec.c:do_execve_common(...).) Поскольку размер буфера рассчитывается не больше, чем размер, необходимый для начальной среды, вы не можете добавить новые переменные, не удалив существующие переменные или не разрушив стек. Таким образом, любая разумная схема, позволяющая вносить изменения в среду процесса, будет использовать кучу, в которой можно выделять и освобождать память произвольных размеров, что в точности делает для вас GNU libc( glibc).

Если процесс использует glibc, то он является POSIX-совместимым, __environпоскольку объявление в glibc//posix/environ.cGlibc инициализируется __environуказателем на память mallocиз кучи процесса, а затем копирует исходную среду из стека в эту область кучи. Каждый раз, когда процесс использует setenvфункцию, glibcвыполняет reallocнастройку размера области, на которую __environуказывает новое значение или переменная. (Вы можете скачать исходный код glibc с помощью git clone git://sourceware.org/git/glibc.git glibc). Чтобы по-настоящему понять механизм, вам также нужно прочитать код Hurd в hurd//init/init.c:frob_kernel_process()(git clone git: //git.sv.gnu.org/hurd/hurd.git hurd).

Теперь, если новый процесс редактируется только forkбез последующей execперезаписи стека, тогда происходит волшебство копирования аргумента и среды linux//kernel/fork.c:do_fork(...), когда copy_processподпрограммы вызывают dup_task_structвыделение стека нового процесса путем вызова alloc_thread_info_node, который вызывает setup_thread_stack( linux//include/linux/sched.h) для нового процесса, использующего alloc_thread_info_node.

Наконец, __environсоглашение POSIX является соглашением пространства пользователя . Он не имеет ничего общего с ядром Linux. Вы можете написать программу пользовательского пространства без использования glibcи без __environглобальных параметров, а затем управлять переменными среды по своему усмотрению. Никто не арестует вас за это, но вам придется написать свои собственные функции управления средой ( setenv/ getenv) и свои собственные оболочки, sys_execи, вероятно, никто не сможет угадать, куда вы вносите изменения в вашу среду.

Джонатан Бен-Авраам
источник
Многие из файлов /proc/[pid]/имеют странную кодировку (кто-то другой может знать, что и почему). Для меня просто cat environвывести переменные окружения в действительно трудном для чтения формате. cat environ | stringsрешил это для меня.
ретро-хакер
@retrohacker Это дает более надежное решение: askubuntu.com/questions/978711/…
Фрэнк
20

Он обновляется по мере того, как и когда процесс получает / удаляет свои переменные окружения. У вас есть ссылка, в которой говорится, что environфайл не обновляется для процесса в его каталоге процессов в / proc файловой системы?

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

или

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

или

ps e -p <pid>

Выше будет печатать переменные среды процесса в psвыходном формате, обработка текста (анализ / фильтрация) требуется, чтобы увидеть переменные среды в виде списка.

Солярис (не спрашивал, но для справки выложу здесь):

/usr/ucb/ps -wwwe <pid>

или

pargs -e <pid> 

РЕДАКТИРОВАТЬ: / proc / pid / environment не обновляется! Я стою исправлено. Процесс проверки ниже. Однако потомки, от которых процесс является fork, наследуют переменную среды процесса, и это видно в их соответствующем файле / proc / self / environment. (Используйте строки)

В оболочке: здесь xargs является дочерним процессом и, следовательно, наследует переменную окружения, а также отражается в его /proc/self/environфайле.

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

Проверка его из другого сеанса, где терминал / сеанс не является дочерним процессом оболочки, в которой установлена ​​переменная среды.

Проверка из другого терминала / сеанса на том же хосте:

Terminal1:: Обратите внимание, что printenv является fork'd и является дочерним процессом bash, и, следовательно, он читает свой собственный файл окружения.

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

терминал2: на том же хосте - не запускайте его в той же оболочке, где была установлена ​​вышеуказанная переменная, запускайте терминал отдельно.

[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
Я делаю export foo=barв одном сеансе bash (pid xxxx), затем делаю cat /proc/xxxx/environ | tr \\0 \\nв другом сеансе bash, и я не вижу foo.
Я обновил приведенный выше ответ примером, проверяющим тот же процесс в оболочке.
Nikhil Mulley
Ты прав. Я стою исправлено. Спасибо. Теперь мне нужно пойти и прочитать мои руководства, чтобы проверить переменные среды другого процесса в пользовательской группе процессов.
Nikhil Mulley
1
Еще одна вещь: я попытался проверить окружение, прикрепляющее gdb к pid, но там до сих пор нет ссылок. Блок переменных среды в памяти перераспределяется всякий раз, когда происходит изменение, и не отражается в файле окружения своего собственного процесса в файловой системе proc, но, тем не менее, позволяет наследоваться дочерним процессом. Это означает, что это может быть легче узнать внутренние детали, когда происходит разветвление, как дочерний процесс получает переменные среды, скопированные как есть.
Nikhil Mulley
Я надеюсь, что @Gilles пролил бы свой свет на этот факел .. :-)
Nikhil Mulley
7

Что ж, следующее не связано с реальными намерениями автора, но если вы действительно хотите «ПРОЧИТАТЬ» /proc/<pid>/environ, вы можете попробовать

strings /proc/<pid>/environ

что лучше, чем catэто.

Фибоначи
источник
1
+1 за strings. Будь проще.
Эд Рэндалл
Согласитесь @EdRandall, это похоже на более легкий подход против xargs --null.
По Лундбергу
«Файл» завершается нулем, заменяет пустые значения на новые строки, и нормальные инструменты снова работают (с обычными оговорками), например:tr '\0' '\n' < /proc/$$/environ | ...
Тор
Пока переменная окружения сама по себе не содержит новых строк. Такие оболочки, как BASH, иногда делают это для «экспортируемых функций».
Энтони