进程与环境变量

2025年5月18日 在想办法用环境变量做程序的配置系统

数据结构

理论上讲应该采用 HashMap 的数据格式保存,以实现 O(1) 的访问速度。

#include <unistd.h>

char **__environ = 0;
weak_alias(__environ, ___environ);
weak_alias(__environ, _environ);
weak_alias(__environ, environ);

// https://git.musl-libc.org/cgit/musl/tree/src/env/__environ.c

但实际使用形如 KEY=VALUE 的字符串数组。

系统调用

对环境变量的读和写均发生在用户态:

#include <stdlib.h>
#include <string.h>
#include <unistd.h>

char *getenv(const char *name)
{
    size_t l = __strchrnul(name, '=') - name;
    if (l && !name[l] && __environ)
        for (char **e = __environ; *e; e++)
            if (!strncmp(name, *e, l) && l[*e] == '=')
                return *e + l+1;
    return 0;
}

// https://git.musl-libc.org/cgit/musl/tree/src/env/getenv.c

仅初始化与系统调用有关:

#include <unistd.h>
#include "syscall.h"

int execve(const char *path, char *const argv[], char *const envp[])
{
    /* do we need to use environ if envp is null? */
    return syscall(SYS_execve, path, argv, envp);
}

// https://git.musl-libc.org/cgit/musl/tree/src/process/execve.c

Bash

在 Bash 中很坏,“环境变量”只是普通变量的一个属性,一般用 export 关键字标识

#define att_exported    0x0000001   /* export to environment */
...
#define att_local       0x0000020   /* variable is local to a function */

// bash/variables.h

/* An array which is passed to commands as their environment.  It is
   manufactured from the union of the initial environment and the
   shell variables that are marked for export. */
char **export_env = (char **)NULL;

// bash/variables.c

所以会出现以下情况,环境变量随变量修改而改变:

export foo=bar
foo=barbar

sh -c 'echo $foo'
# output: barbar

其它对环境变量的构建和修改与普通函数无异

static inline char *
mk_env_string (name, value, attributes)
     const char *name, *value;
     int attributes;
{
    ...
    {
      p = (char *)xmalloc (2 + name_len + value_len);
      memcpy (p, name, name_len);
      q = p + name_len;
    }
    q[0] = '=';

	memcpy (q + 1, value, value_len + 1);
}

// bash/variables.c


int
shell_execve (command, args, env)
     char *command;
     char **args, **env;
{
    ...
    execve (command, args, env);
}

// bash/execute_cmd.c