Linux /proc/<pid>/environ
不会更新(据我所知,该文件包含进程的初始环境)。
如何阅读流程的当前环境?
Linux /proc/<pid>/environ
不会更新(据我所知,该文件包含进程的初始环境)。
如何阅读流程的当前环境?
Answers:
/proc/$pid/environ
如果流程更改了自己的环境,则不会更新。但是许多程序不会费心更改自己的环境,因为这样做没有意义:一个程序的环境只有通过/proc
和才能通过常规渠道看到ps
,甚至不是每个unix变体都具有这种功能,因此应用程序不依赖在上面。
就内核而言,环境仅作为execve
启动程序的系统调用的参数出现。Linux通过公开了一个内存区域/proc
,有些程序更新了该区域,而另一些则没有。特别是,我认为没有任何外壳程序可以更新此区域。由于区域的大小固定,因此无法添加新变量或更改值的长度。
PATH=foo
在shell中编写并不意味着该shell将要修改*envp
。在某些shell中,这仅更新了内部数据结构,而外部程序执行代码则更新了*envp
。看看assign_in_env
在variables.c
在bash的来源,例如。
fork
则libc sys_fork
使用为子进程分配堆的环境进行调用。
argv
比较常见,但都存在)。
您可以从中读取流程的初始环境/proc/<pid>/environ
。
如果流程更改了环境,则为了读取环境,您必须具有该流程的符号表,并使用ptrace
系统调用(例如通过使用gdb
)从全局char **__environ
变量读取环境。没有其他方法可以从正在运行的Linux进程中获取任何变量的值。
那就是答案。现在为一些笔记。
上面假设该过程符合POSIX,这意味着该过程使用Ref Spec中char **__environ
指定的全局变量来管理其环境。
流程的初始环境在流程堆栈上的固定长度缓冲区中传递给流程。(执行此操作的通常机制是linux//fs/exec.c:do_execve_common(...)
。)由于计算得出的缓冲区大小不超过初始环境所需的大小,因此,如果不擦除现有变量或破坏堆栈,就无法添加新变量。因此,任何允许在流程环境中进行更改的合理方案都将使用堆,可以在其中分配和释放任意大小的内存,这正是GNU libc
(glibc
)为您所做的。
如果该进程使用glibc
,则它是POSIX兼容的,并__environ
在glibc//posix/environ.c
Glibc中进行声明,__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_stack
(linux//include/linux/sched.h
)新进程alloc_thread_info_node
。
最后,POSIX __environ
约定是用户空间约定。它与Linux内核中的任何内容都没有任何关系。你可以写一个用户空间程序,而无需使用glibc
和没有__environ
全球和但此后管理环境变量,你喜欢。没有人会因为这样做而逮捕您,但是您将不得不编写自己的环境管理功能(setenv
/ getenv
)和自己的包装器,sys_exec
而且很可能没人会猜测您将更改放在何处。
/proc/[pid]/
似乎都有奇怪的编码(其他人可能知道什么以及为什么)。对我而言,只需cat environ
以一种非常难以阅读的格式打印出环境变量即可。 cat environ | strings
为我解决了这个问题。
进程在获取/删除其环境变量时会对其进行更新。您是否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 ~]$
export foo=bar
在一个bash的会话(pid xxxx)中进行操作,然后cat /proc/xxxx/environ | tr \\0 \\n
在另一个bash的会话中进行操作,但我没有看到foo
。
gdb
pid 附加到的环境,但在那里仍然没有引用。只要有更改,就将重新分配内存中的环境变量块,并且该环境变量块不会反映在proc文件系统中其自身进程的环境文件中,但是允许子进程继承。这意味着当派生发生时,可以更容易地了解内部细节,子进程如何获取原样复制的环境变量。
好吧,以下内容与作者的真实意图无关,但是,如果您确实想“阅读”本书/proc/<pid>/environ
,可以尝试
strings /proc/<pid>/environ
比cat
这更好。
strings
。把事情简单化。
xargs --null
。
tr '\0' '\n' < /proc/$$/environ | ...