如何读取进程的环境变量

如何读取进程的环境变量

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__environmallocsetenvglibcrealloc__environgit clone git://sourceware.org/git/glibc.git glibchurd//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.halloc_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如果进程更改其自身环境,则会更新。但是很多程序都懒得去改变自己的环境,因为这有点毫无意义:程序的环境通过正常渠道是不可见的,只能通过/procps,甚至不是每个unix变体都有这种功能,所以应用程序不依赖在上面。

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

答案4

我选择暂时将一个gdb会话附加到该程序并getenv从那里调用。它可能不适用于全部程序,而且成本相当高,但这通常有效:

echo 'p (char *) getenv("PWD")' | \
     gdb --quiet -p $(pidof emacs) | \
     sed -n 's/.*"\(.*\)"$/\1/p'

相关内容