Unix环境变量

概述

在Unix中,每个进程都有自己的一组环境变量,这些环境变量,要么是一组全局字符串,要么是子进程从父进程继承而来的,如果子进程不对其修改则与父进程的环境变量一模一样。
Unix内核并不查看这些字符串,它们的解释权完全取决于各个应用程序。例如shell是Unix中一个可执行程序,通常shell的启动文件中会对环境变量进行设置。所以当我们进入shell之后可以查看path等环境变量。在当前shell中启动的进程会继承其父进程shell的环境变量,也就可以查看path等环境变量,环境变量可以在登录的时候自动设置,也可以由用户自行设置。

环境变量相关变量

每个程序都会接收到一张环境表。与参数表一样,环境表也是一个字符指针数组。其中每个指针都包含一个以NULL结尾的字符串的地址。全局变量
environ指向了这个数组的地址。

代码如下:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
extern char **environ;
if (environ!= NULL) {
for (i = 0; environ[i] != NULL; i++) {
printf("env: %s\n", environ[i]);
}
}

环境变量相关的函数

与环境变量相关的函数包括以下几种:取环境变量的值,添加环境变量、修改环境变量、以及删除环境变量.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdlib.h>
//获取环境变量的值
char* getenv(const char* name); //若包含此key返回对应的值,否则返回NULL
/*
* function: 修改环境变量的值 前面两个分别为键值对,最后一个表示是否需要删除原有的定义重写。
* params:键没有存在,则创建此键值对。若键已经存在:rewrite=0时不覆盖原来的值;rewrite!=0覆盖原来的值
* return:成功返回0, 出错返回非0。
*/
int setenv(const char* name, const char* value, int rewrite);
/*
* function: 添加环境变量,若存在则删除原有的,添加新的,不存在则直接添加
* params: 参数为一个键值对字符串,如"name=test"
* return: 成功返回0,不成功返回-1
*/
int putenv(char *str);
/*
* function: 删除name的定义,即使不存在也不出错。
* params: 参数为键
* return:出错返回-1,不出错返回0
*/
int unsetenv(const char*name);
/*
* function: 清除所有的环境变量
* return: 成功返回0, 失败返回-1。
*/
int clearenv();

putenv和setenv的区别

putenv可以使用程序中已经定义的且形如”name=value”的字符串作为参数。此时系统不再为该环境变量分配内存,环境变量将使用程序中定义变量的内存。
并将该字符串的地址保存在环境变量中。所以要使用putenv一定要用全局变量作为参数,否则程序退出栈内存被释放,再次访问环境变量将会出现未定义行为,
导致环境变量不可用。

putenv也可用字符串常量做参数,这个时候系统将为其分配内存。

但是setenv去设置环境变量系统将会先malloc出一块内存给环境变量使用,所以此时不需要担心环境不可用的情况。

环境变量在进程空间中的存储位置

环境变量和环境字符串通常放在进程存储空间的顶部,也就是栈内存之上

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
extern char **environ;
int main() {
int i;
printf("the address of the environment: %p\n", environ);
printf("the adress of first i: %p\n", &i);
}

环境变量中进行增删改操作的实现机制

删除环境变量比较容易,当增加或者修改环境变量的时候由于环境表和环境字符串通常占用的是进程地址空间的顶部,所以它不能再向
高地址(向上)扩展,同时也不能在移动在它之下的各栈帧,所以也不能向下扩展。两者的组合使得该空间的长度不能再增加。

  • 删除环境变量:删除环境变量时只需要先找到该指针,然后将所有后续指针都向环境表的首部顺序移一个位置。
  • 修改环境变量:
    • 若新的环境变量value长度小于或者等于原有的值,则直接将其复制到旧值。
    • 否则,先调用malloc在堆上分配一块内存,然后将新字符串指向该空间,接着使环境变量表中针对name的指针指向新分区。
  • 增加环境变量: 增加新环境变量比较复杂。必须首先通过调用malloc为新的name=value分配内存空间,然后将字符串复制到此空间中。
    • 如果该name是第一次增加,则必须调用malloc为新的指针表分配内存空间,然后将原来的环境表复制到新的内存,并将指向新的name=value字符串
      的指针存放在该指针表的表尾,然后将空指针放在其后面。最后使environ指向新的环境表。这样就导致原来位于栈顶之上的环境表移到了堆内存中。
      但是大多数的环境指针仍然指向栈顶之上的name=value字符串。
    • 如果不是第一次新增加一个name,可知之前已经将环境表迁移到堆内存中,所以只需要调用realloc,以分配比原空间多存放一个指针的空间。然后将指针指向name=value
      字符串的指针,最后是一个NULL指针。

Comment and share

  • page 1 of 1

魏传柳(2824759538@qq.com)

author.bio


Tencent


ShenZhen,China