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

例子:

$ export FOO=42
$ <<< 'int main() {printf("%s\n", getenv("FOO"));}' gcc -w -xc - && ./a.exe
42

在 libc 中, environ 变量被声明为 environ.c 。我希望它在执行时为空,但我得到 42

更进一步的 getenv 可以简化如下:
char * getenv (const char *name)
{
    size_t len = strlen (name);
    char **ep;
    uint16_t name_start;

    name_start = *(const uint16_t *) name;
    len -= 2;
    name += 2;

    for (ep = __environ; *ep != NULL; ++ep)
    {
        uint16_t ep_start = *(uint16_t *) *ep;

        if (name_start == ep_start && !strncmp (*ep + 2, name, len)
                && (*ep)[len + 2] == '=')
            return &(*ep)[len + 3];
    }
    return NULL;
}
libc_hidden_def (getenv)

这里我只获取 __environ 变量的内容。但是我从来没有初始化它。

所以我很困惑,因为 environ 应该是 NULL 除非我的主函数不是我程序的真正入口点。也许 gcc 通过添加作为标准 C 库一部分的 _init 函数来提醒我。
environ 在哪里初始化?

最佳答案

这里没有什么神秘之处。

首先,shell fork 。 Forked 进程显然具有相同的环境。然后在子进程中执行一个新程序。有问题的系统调用是 execve ,它接受一个指向环境的指针。

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

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

编辑: 因为问题被编辑为询问 environ :

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

再一次,所有这些的来源都是免费的。虽然 glibc 的源代码由于格式恶劣而难以阅读,但 BSD 的源代码很容易并且在概念上足够等效。

http://code.metager.de/source/xref/freebsd/libexec/rtld-elf/rtld.c#389

关于c - 程序如何继承环境变量?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31034993/

10-13 07:15