如何读取流程的环境变量


Answers:


20

/proc/$pid/environ如果流程更改了自己的环境,则不会更新。但是许多程序不会费心更改自己的环境,因为这样做没有意义:一个程序的环境只有通过/proc和才能通过常规渠道看到ps,甚至不是每个unix变体都具有这种功能,因此应用程序不依赖在上面。

就内核而言,环境仅作为execve启动程序的系统调用的参数出现。Linux通过公开了一个内存区域/proc,有些程序更新了该区域,而另一些则没有。特别是,我认为没有任何外壳程序可以更新此区域。由于区域的大小固定,因此无法添加新变量或更改值的长度。


因此,实际上没有办法访问进程的* envp(指向环境设置的指针数组)。@Gilles,能否请您显示是否可以连接调试器并读取指向环境设置的指针数组。
Nikhil Mulley 2012年

2
@Nikhil当然可以。但是仅仅因为您PATH=foo在shell中编写并不意味着该shell将要修改*envp。在某些shell中,这仅更新了内部数据结构,而外部程序执行代码则更新了*envp。看看assign_in_envvariables.c在bash的来源,例如。
吉勒斯(Gilles)'所以

9
@吉尔斯:这个答案充其量是误导的(-1)。从进程的堆栈中读取/ proc / $$ / environ中的环境。参见fs / proc / base.c。这是初始环境。它永远不会更新,实际上却无法更新。libc setenv使用的环境在堆上分配,并使用堆栈中环境的内容进行初始化。如果进程调用libc的,fork则libc sys_fork使用为子进程分配堆的环境进行调用。
乔纳森·本·阿夫拉罕

7
@ JonathanBen-Avraham是正确的,没有在任何shell中更新初始环境。但是,该区域不仅在Linux下是只读的,我遇到了使用该区域报告状态的程序(状态报告通过argv比较常见,但都存在)。
吉尔斯(Gillles)“所以别再作恶了”

39

您可以从中读取流程的初始环境/proc/<pid>/environ

如果流程更改了环境,则为了读取环境,您必须具有该流程的符号表,并使用ptrace系统调用(例如通过使用gdb)从全局char **__environ变量读取环境。没有其他方法可以从正在运行的Linux进程中获取任何变量的值。

那就是答案。现在为一些笔记。

上面假设该过程符合POSIX,这意味着该过程使用Ref Spec中char **__environ指定的全局变量来管理其环境。

流程的初始环境在流程堆栈上的固定长度缓冲区中传递给流程。(执行此操作的通常机制是linux//fs/exec.c:do_execve_common(...)。)由于计算得出的缓冲区大小不超过初始环境所需的大小,因此,如果不擦除现有变量或破坏堆栈,就无法添加新变量。因此,任何允许在流程环境中进行更改的合理方案都将使用堆,可以在其中分配和释放任意大小的内存,这正是GNU libcglibc)为您所做的。

如果该进程使用glibc,则它是POSIX兼容的,并__environglibc//posix/environ.cGlibc中进行声明,__environ并使用指向malloc该进程堆中的内存的指针进行初始化,然后将堆栈中的初始环境复制到此堆区域中。每次该过程使用该setenv函数时,都会glibc执行a 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_stacklinux//include/linux/sched.h)新进程alloc_thread_info_node

最后,POSIX __environ约定是用户空间约定。它与Linux内核中的任何内容都没有任何关系。你可以写一个用户空间程序,而无需使用glibc和没有__environ全球和但此后管理环境变量,你喜欢。没有人会因为这样做而逮捕您,但是您将不得不编写自己的环境管理功能(setenv/ getenv)和自己的包装器,sys_exec而且很可能没人会猜测您将更改放在何处。


其中的许多文件/proc/[pid]/似乎都有奇怪的编码(其他人可能知道什么以及为什么)。对我而言,只需cat environ以一种非常难以阅读的格式打印出环境变量即可。 cat environ | strings为我解决了这个问题。
retrohacker

@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输出格式打印过程的环境变量,需要文本处理(解析/过滤)才能将环境变量视为列表。

Solaris(未要求,但我将在此处提供参考):

/usr/ucb/ps -wwwe <pid>

要么

pargs -e <pid> 

编辑: / proc / pid / environ没有更新!我站得住了。验证过程如下。但是,从中派生该进程的子级将继承该进程环境变量,并且该变量在其各自的/ proc / self / environ文件中可见。(使用字符串)

在外壳中具有:这里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]$

从其他会话(终端/会话不是设置了环境变量的shell的子进程)检查它。

从同一主机上的另一个终端/会话进行验证:

terminal1::请注意,printenv是派生的,并且是bash的子进程,因此它将读取自己的环境文件。

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

terminal2:在同一主机上-不要在设置了上述变量的同一shell中启动它,请分别启动终端。

[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

我用检查Shell中相同过程的示例更新了以上答案。
Nikhil Mulley 2012年

你是对的。我站得住了。谢谢。现在,我必须去阅读手册,以检查用户进程组中其他进程的环境变量。
Nikhil Mulley 2012年

1
还有一件事:我尝试检查将gdb pid 附加到的环境,但在那里仍然没有引用。只要有更改,就将重新分配内存中的环境变量块,并且该环境变量块不会反映在proc文件系统中其自身进程的环境文件中,但是允许子进程继承。这意味着当派生发生时,可以更容易地了解内部细节,子进程如何获取原样复制的环境变量。
Nikhil Mulley 2012年

我希望@吉尔斯(Gilles)会对此提出一些看法。:-)
尼克希尔·穆利

7

好吧,以下内容与作者的真实意图无关,但是,如果您确实想“阅读”本书/proc/<pid>/environ,可以尝试

strings /proc/<pid>/environ

cat这更好。


1
为+1 strings。把事情简单化。
Ed Randall

同意@EdRandall,这就像vs相比更简单xargs --null
Per Lundberg '18

“文件”以null结尾,用换行符替换为null,正常工具再次起作用(带有通常的警告),例如:tr '\0' '\n' < /proc/$$/environ | ...
Thor
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.