Linux/proc/<pid>/environ
不会更新进程的环境。据我了解,该文件包含进程的初始环境。
我如何读取进程的当前的环境?
答案1
您可以阅读最初的进程的环境来自/proc/<pid>/environ
.
如果一个进程变化它的环境,那么为了读取环境,您必须拥有该进程的符号表并使用ptrace
系统调用(例如通过使用gdb
)从全局变量中读取环境char **__environ
。没有任何其他方法可以从正在运行的 Linux 进程获取任何变量的值。
这就是答案。现在做一些笔记。
上面假设进程是 POSIX 兼容的,这意味着进程使用全局变量来管理其环境,char **__environ
如参考规格。
进程的初始环境通过进程堆栈上的固定长度缓冲区传递给进程。 (执行此操作的常用机制是linux//fs/exec.c:do_execve_common(...)
。)由于计算得出的缓冲区大小不超过初始环境所需的大小,因此在不擦除现有变量或破坏堆栈的情况下无法添加新变量。因此,任何允许更改进程环境的合理方案都将使用堆,其中可以分配和释放任意大小的内存,这正是 GNU libc
( glibc
) 为您所做的。
如果进程使用glibc
,那么它是 POSIX 兼容的,在Glibc__environ
中声明,使用指向进程堆中内存的指针进行初始化,然后将初始环境从堆栈复制到该堆区域中。每次进程使用该函数时,都会调整指向的区域的大小以容纳新值或变量。 (您可以通过 下载 glibc 源代码)。要真正理解该机制,您还必须阅读 Hurd 代码(git clone git://git.sv.gnu.org/hurd/hurd.git hurd)。glibc//posix/environ.c
__environ
malloc
setenv
glibc
realloc
__environ
git clone git://sourceware.org/git/glibc.git glibc
hurd//init/init.c:frob_kernel_process()
现在,如果仅创建了新进程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
并且很可能没有人能够猜测您将环境更改放在哪里。
答案2
当进程获取/删除其环境变量时,它会被更新。您是否有参考文献表明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 文件中可见。 (使用字符串)
在 shell 中:这里 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 的子进程。
从同一主机上的另一个终端/会话进行验证:
1号航站楼::请注意 printenv 是 fork 的,并且是 bash 的子进程,因此它读取自己的环境文件。
[centos@centos t]$ echo $$
2610
[centos@centos t]$ export SPIDEY=NIKHIL
[centos@centos t]$ printenv | grep SPIDEY
SPIDEY=NIKHIL
[centos@centos t]$
航站楼2:在同一主机上 - 不要在设置上述变量的同一 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 ~]$
答案3
/proc/$pid/environ
如果进程更改其自身环境,则会更新。但是很多程序都懒得去改变自己的环境,因为这有点毫无意义:程序的环境通过正常渠道是不可见的,只能通过/proc
和ps
,甚至不是每个unix变体都有这种功能,所以应用程序不依赖在上面。
就内核而言,环境仅作为参数出现execve
启动程序的系统调用。 Linux 通过 公开内存中的一个区域/proc
,有些程序会更新该区域,而另一些则不会。特别是,我认为没有任何 shell 会更新这个区域。由于该区域具有固定大小,因此不可能添加新变量或更改值的长度。
答案4
我选择暂时将一个gdb
会话附加到该程序并getenv
从那里调用。它可能不适用于全部程序,而且成本相当高,但这通常有效:
echo 'p (char *) getenv("PWD")' | \
gdb --quiet -p $(pidof emacs) | \
sed -n 's/.*"\(.*\)"$/\1/p'