为何/proc文件系统中environ内容与extern environ指向内容不一致?
/proc/environ不一致的原因 你遇到的这个问题既不是getenv()的问题,也不是/proc的问题,核心原因是FastCGI(mod_fcgid)的运行机制导致进程环境变量被动态修改,而/proc/${PID}/environ只保留了进程启动时的初始环境快照。
先拆解你的现象:
- 从
/proc输出看,进程启动时的环境变量只有基础的PATH和LD_LIBRARY_PATH:
shell@kernel # xargs --null --max-args=1 echo < /proc/${PID_OF_THE_RUNNING_PROCESS}/environ PATH=/usr/bin:/bin:/usr/sbin:/sbin LD_LIBRARY_PATH=/usr/local/httpd-2.2.19/lib:
- 但你的程序通过
extern environ输出的却是当前HTTP请求对应的CGI环境变量(比如FCGI_ROLE、HTTP_HOST、REMOTE_ADDR等),甚至最后还有一行格式异常的declare -x 112.96.194.222(这应该是环境变量数组循环处理时的边界小问题)。
为什么会出现这种差异?
FastCGI进程的启动与运行逻辑
Apache的mod_fcgid模块会预先启动一批FastCGI进程(就是你看到PID的那些),这些进程启动时只加载系统基础环境变量(就是/proc里看到的那些),之后会驻留在后台等待请求。
当有HTTP请求过来时,mod_fcgid会通过IPC(套接字)把当前请求对应的CGI环境变量(比如请求头、请求参数相关的变量)发送给FastCGI进程,FastCGI的运行库(你代码依赖的fcgi相关库)会动态修改进程的environ数组,把这些请求相关的环境变量加载进去,供你的程序使用。/proc/environ的本质/proc/${PID}/environ存储的是进程创建时刻的环境变量快照,一旦进程启动后,哪怕进程自己通过putenv()、setenv()或者内部代码修改了environ指针指向的内容,/proc里的内容都不会更新——它就是进程出生时的环境记录,不会跟着进程运行中的变化而改变。getenv()与extern environ的一致性getenv()就是从environ指针指向的当前环境变量数组里查找内容,所以它和你直接遍历environ得到的结果是完全一致的,二者都反映的是进程当前的、被动态修改后的环境变量,而不是初始环境。
总结
/proc/${PID}/environ是对的:它记录了FastCGI进程启动时的初始环境;getenv()和extern environ也是对的:它们访问的是进程当前的、已经被FastCGI运行库更新为请求相关的环境变量;- 二者不一致的根源是FastCGI进程运行过程中环境变量被动态修改,而
/proc保留的是初始快照。
附你提供的相关代码与日志
进程代码
#include<stdio.h> extern char **environ; void run() { char* s = *environ; printf("declare -x all env begin\n"); for (int i = 1; NULL != s; ++i) { printf("declare -x %s\n", s); s = *(environ+i); } printf("declare -x all env end\n"); }
进程控制台日志
declare -x all env begin declare -x FCGI_ROLE=RESPONDER declare -x UNIQUE_ID=Wvq-Cn8AAAEAAAkmJlsAAAmM declare -x HTTP_HOST=www.example.com declare -x HTTP_X_CLIENT_PROTO=https declare -x HTTP_X_CLIENT_PROTO_VER=HTTP/1.1 declare -x HTTP_X_REAL_IP=112.96.194.222 declare -x HTTP_X_FORWARDED_FOR=112.96.194.222 declare -x CONTENT_LENGTH=177 declare -x HTTP_CHARSET=utf-8 declare -x HTTP_ACCEPT_ENCODING=gzip declare -x HTTP_REFERER=https://serviceexample.com/exbb58374cdce267a6/91/page-frame.html declare -x CONTENT_TYPE=application/x-www-form-urlencoded declare -x HTTP_USER_AGENT=Mozilla/5.0 (Linux; Android 5.1; vivo X6Plus D Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/044030 Mobile Safari/537.36 MicroMessenger/6.6.6.1300(0x26060637) NetType/4G Language/zh_CN MicroMessenger/6.6.6.1300(0x26060637) NetType/4G Language/zh_CN declare -x PATH=/usr/bin:/bin:/usr/sbin:/sbin declare -x SERVER_SIGNATURE= declare -x SERVER_SOFTWARE=Apache/2.2.19 (Unix) mod_ssl/2.2.19 OpenSSL/1.0.1t DAV/2 mod_fcgid/2.3.9 declare -x SERVER_NAME=www.example.com declare -x SERVER_ADDR=10.241.94.209 declare -x SERVER_PORT=80 declare -x REMOTE_ADDR=10.56.81.214 declare -x DOCUMENT_ROOT=/data/doc/www.example.com/htdocs declare -x SERVER_ADMIN=webmaster@tencent.com declare -x SCRIPT_FILENAME=/data/doc/www.example.com/cgi-bin/ex/common/www_ex_time.cgi declare -x REMOTE_PORT=46151 declare -x GATEWAY_INTERFACE=CGI/1.1 declare -x SERVER_PROTOCOL=HTTP/1.1 declare -x REQUEST_METHOD=POST declare -x QUERY_STRING= declare -x REQUEST_URI=/cgi-bin/ex/common/www_ex_time.cgi declare -x SCRIPT_NAME=/cgi-bin/ex/common/www_ex_time.cgi declare -x HTTP_CONNECTION=close declare -x all env end declare -x 112.96.194.222
内容的提问来源于stack exchange,提问作者http8086

