C 程序如何继承环境变量?

C 程序如何继承环境变量?,c,linux,libc,C,Linux,Libc,当我使用标准C库中的函数getenv()时,我的程序从其父级继承环境变量 例如: $ export FOO=42 $ <<< 'int main() {printf("%s\n", getenv("FOO"));}' gcc -w -xc - && ./a.exe 42 在这里,我将只获取\uuu environ变量的内容。然而,我从未初始化它 所以我感到困惑,因为environ应该是NULL,除非我的主函数不是程序的真正入口点。也许,gcc添加了一个\u i

当我使用标准C库中的函数
getenv()
时,我的程序从其父级继承环境变量

例如:

$ export FOO=42
$ <<< 'int main() {printf("%s\n", getenv("FOO"));}' gcc -w -xc - && ./a.exe
42
在这里,我将只获取
\uuu environ
变量的内容。然而,我从未初始化它

所以我感到困惑,因为
environ
应该是
NULL
,除非我的主函数不是程序的真正入口点。也许,
gcc
添加了一个
\u init
函数,它是标准C库的一部分


environ
在哪里初始化?

调用程序(shell)的父进程定义了FOO。新创建的进程从父进程接收副本。

这里没有什么神秘之处

首先,贝壳分叉。分叉过程显然具有相同的环境。然后在子系统中执行一个新程序。所讨论的系统调用是
execve
,它接受指向环境的指针

因此,在执行二进制文件后设置什么环境完全取决于执行该二进制文件的代码

所有这些都可以通过运行strace轻松看到

编辑:由于问题被编辑为询问有关
环境的问题

当您执行一个动态链接的二进制文件时,执行任何操作的第一个用户空间代码都来自加载程序。加载器设置变量,如
argc
argv
environ
,然后才从二进制文件调用
main()

再一次,所有这些信息的来源都是免费的。虽然glibc的源代码由于糟糕的格式很难阅读,但BSD的源代码很简单,并且在概念上相当


问题是,shell如何运行命令

答案是使用
fork()
和创建一个新进程,这将创建一个与当前进程具有相同环境的进程

但是,您可以使用创建具有自定义环境的新流程


但在任何正常情况下,这都是不必要的,特别是因为许多程序希望定义一些环境变量,例如
PATH
,通常,子进程从调用它的环境继承环境变量。

环境变量作为第三个参数从父进程传递到
main
。要发现这一点,最简单的方法是阅读系统调用的文档,尤其是这一位:

说明

execve()
执行文件名所指向的程序。[...]
argv
是传递给新程序的参数字符串数组。按照惯例,这些字符串中的第一个应该包含与正在执行的文件关联的文件名
envp
是一个字符串数组,通常采用
key=value
的形式,作为环境传递给新程序。
argv
envp
都必须由空指针终止。参数向量和环境可由被调用程序的主函数访问,其定义如下:

int main(int argc, char *argv[], char *envp[])
C库在调用
main
之前,将
envp
参数复制到启动代码中的
environ
全局变量中:例如,GNU libc在中执行此操作,musl libc在中执行此操作。(您可能会发现musl libc的代码比GNU libc的代码更容易跟踪。)相反,如果您使用一个不带显式环境向量的包装函数启动程序,C库会提供
environ
作为
execve
的第三个参数。因此,环境变量的继承严格来说是一种用户空间约定。就内核而言,每个程序接收两个参数向量,它不关心其中包含什么


(请注意,三个参数
main
是C语言的扩展。C标准仅指定
intmain(void)
intmain(intargc,char**argv)
,但它允许实现定义其他表单()。三个参数
main
是自Unix V7以来环境变量的工作方式(如果不是更长的话),Microsoft也对此进行了记录-请参见。)

在Linux下,当程序启动时,其参数和环境变量存储在堆栈中。对于C程序,在
main
之前执行的代码会查看这一点,生成
argv
envp
指针数组,然后使用这些值调用
main
(和
argc

当程序调用
execvpe
以转换为新程序(通常在调用
fork
之后)时,会传入一个
envp
,以及一个
argv
。内核将把这些数据复制到新程序的堆栈中


当调用任何其他
exec
函数时,glibc将把当前程序的
environ
作为新程序的
envp
传递到
execvpe
(或直接传递到sys_exec)。

那么环境变量的意义是什么?我认为这是正确的。您在运行程序之前定义了
FOO
,因此它是可访问的@iharob有很多用途,只是有点特定于运行的程序。这些变量的目的是存在于在给定环境下运行的任何程序中,但是您可以创建一个具有干净环境的进程,但是使用环境变量的要点是,任何子进程都可以从子进程中访问它们,因此shell当然会使用自己环境的副本创建新进程。您在这里引用的是哪个C库实现?@zwol我不知道
strace
这非常有用。Ca
int execve(const char *filename, char *const argv[], char *const envp[]);
int main(int argc, char *argv[], char *envp[])