如何讀取程序的環境變數
Linux
/proc/<pid>/environ
不會更新程序的環境。據我了解,該文件包含該過程的初始環境。如何讀取程序的目前環境?
/proc/$pid/environ
如果程序改變了自己的環境,它會更新。但是很多程序都懶得去改自己的環境,因為這有點沒意義:一個程序的環境通過普通的渠道是看不到的,只能通過/proc
andps
,甚至不是每個unix變種都有這種特性,所以應用程序不依賴在上面。就核心而言,環境僅作為
execve
啟動程序的系統呼叫的參數出現。Linux 通過 暴露記憶體中的一個區域/proc
,有些程序會更新這個區域,而有些程序則不會。特別是,我認為沒有任何 shell 更新這個區域。由於該區域具有固定大小,因此無法添加新變數或更改值的長度。
您可以從 中讀取程序的初始環境
/proc/<pid>/environ
。如果一個程序改變了它的環境,那麼為了讀取環境,你必須有程序的符號表並使用
ptrace
系統呼叫(例如使用gdb
)從全域char **__environ
變數中讀取環境。沒有任何其他方法可以從正在執行的 Linux 程序中獲取任何變數的值。這就是答案。現在做一些筆記。
以上假設程序是 POSIX 兼容的,這意味著程序使用Ref Spec
char **__environ
中指定的全域變數來管理其環境。程序的初始環境在程序堆棧上的固定長度緩衝區中傳遞給程序。(執行此操作的常用機制是
linux//fs/exec.c:do_execve_common(...)
。)由於緩衝區的大小被計算為不超過初始環境所需的大小,因此您不能在不刪除現有變數或破壞堆棧的情況下添加新變數。因此,任何允許更改程序環境的合理方案都將使用堆,其中可以分配和釋放任意大小的記憶體,這正是 GNUlibc
(glibc
) 為您所做的。如果程序使用
glibc
,那麼它是 POSIX 兼容的,__environ
在glibc//posix/environ.c
Glibc中聲明__environ
它使用指向程序堆中的記憶體的指針進行初始化malloc
,然後將初始環境從堆棧複製到該堆區域。每次程序使用該setenv
函式時,都會調整指向的區域glibc
的realloc
大小__environ
以適應新的值或變數。(您可以使用 下載 glibc 原始碼git clone git://sourceware.org/git/glibc.git glibc
)。要真正理解該機制,您還必須閱讀hurd//init/init.c:frob_kernel_process()
(git clone git://git.sv.gnu.org/hurd/hurd.git hurd) 中的 Hurd 程式碼。現在,如果新程序只是
fork
ed,而沒有隨後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
而且很可能沒有人能夠猜出您將更改放在環境中的位置。