免费PDF文档下载地址:http://ishare.iask.sina.com.cn/f/36661080.html
一、什么是环境变量?
环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,形式如下
name=value
Linux是一个多用户的操作系统。每个用户登录系统后,都会有一个专用的运行环境。通常每个用户默认的环境都是相同的,这个默认环境实际上就是一组环境变量的定义。
每个程序都会接收到一张environment list。与参数表一样,environment list也是一个字符指针数组,其中每个指针包含一个以null结束的C字符串的地址。全局变量environ则包含了该指针数组的地址:
extern char **environ;
例如,如果该环境包含5个字符串,那么它看起来可能如下图。我们称environ为environment pointer,指针数组为environment list, 其中各指针指向的字符串为environment strings。
二、环境变量的作用
环境变量一般用来控制程序的行为。
例如PATH:当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找程序外,还应到PATH中指定的路径去找。
所以,用户通过设置环境变量,来更好地运行程序。
此外,子进程继承父进程的环境,所有环境变量也是父子进程交流的一种方式哦
三、环境变量相关的函数
#include <stdlib.h>
char *getenv(const char *name);
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);
int putenv(char *string);
int clearenv(void);
获得环境变量值的函数:
最简单也最常用的当然是getenv函数。
参数是环境变量名name,例如”HOME”或者”PATH”。如果环境变量存在,那么getenv函数会返回环境变量值,即value的首地址;如果环境变量不存在,那么getenv函数返回NULL
来看个例子:
char *pathval;
/* Look for PATH; if not present, supply a default value */
if ((pathval = getenv(“PATH”)) == NULL)
pathval = “/bin:/usr/bin:/usr/ucb”;
使用getenv函数,我们需要注意两点:
① 存在这样一种情况,就是环境变量存在,但是其value却是个空字符串 —— 这种情况下,getenv不会返回NULL,而是该空字符串的首地址。 所以,我们在程序中,在使用返回值之前不但要检查getenv函数返回是否为NULL,还要检测返回的字符串是否为空字符串。
② 对getenv函数返回的值要小心处理,因为一旦修改了,那么就改动了当前进程的环境。
修改或添加环境变量的函数:
有时候我们想修改一个已经存在于系统中的环境变量或者添加一个新的环境变量,那么我们可以使用setenv函数或者putenv函数,不过这两个函数之间的差别还是比较大的,我们应该时刻小心。
if (setenv(“PATH”, “/bin:/usr/bin:/usr/ucb”, 1) != 0)
{
/* handle failure */
}
setenv将name设置成value。
1.如果name在环境中不存在,那么很好办,在环境中添加这个新的变量就OK。
2.如果在环境中name已经存在,那么
(a)若overwrite非0,那么更新name的value(实质是更新环境表,指向新的value);
(b)若overwrite为0,则环境变量name不变,并且也不出错。
第1种情况,即name在环境中不存在,那么setenv函数必须在environment list中增加一个新的entry,然后动态申请存储空间来存储name=value,并且使entry指向该空间。
第2种情况,即name在环境中已存在,那么setenv函数不必在environment list中增加一个新的entry。当overwrite为0, 则不必改动entry的指向;当overwrite非0, 则直接使该entry指向name=value,当然该name=value也是存储在动态申请的内存里。
另一个可供选择的函数是putenv函数。
if (putenv(“PATH=/bin:/usr/bin:/usr/ucb”) != 0)
{
/* handle failure */
}
putenv取形式为name=value的字符串,直接将其放到环境表中,直接的意思是environment list中对应的entry直接指向传递进来的name=value,而无需动态申请内存来存放name=value
1. 如果name在环境中不存在,那么很好办,在环境中添加这个新的变量就OK。
2. 如果在环境中name已经存在,那么更新name的value(实质是更新环境表,指向新的value)
第1种情况,即name在环境中不存在,那么putenv函数必须在environment list中增加一个新的entry,然后是该entry直接指向name=value
第2种情况,即name在环境中已存在,那么putenv函数不必在environment list中增加一个新的entry,而是修改这个entry,使其指向name=value
可见自始至终,putenv函数都没有动态申请内存,而是直接将传递进来的字符串地址作为参数直接放入environment list中。由此可知,将存在栈中的字符串(即局部变量)作为参数传递给putenv函数会发生错误,其原因是,从当前函数返回时,其栈帧占用的存储区可能被重用。
删除环境变量的函数:
如果需要删除一个环境变量,那么使用unsetenv函数:
unsetenv(“PATH”);
如果因程序安全因素的考虑,需要建立程序自己的环境参数,那么使用clearenv函数来删除整个环境
if (clearenv() != 0)
{
/* handle failure */
}
不过clearenv函数不是POSIX标准制定的,所以如果clearenv在你的系统上没有,也可以直接设置environ = NULL;来达到相同的目的。
值得一提的是,当只提供name给putenv函数时,也能达到删除该环境变量的作用。
四、深入以上函数对environment list的操作
最后,让我们通过实例调试来实际看看这些函数到底做了哪些操作!
首先考虑的简单的情况 —— 修改一个现有的name
实例如下getenv_test.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage:%s + string.\n", argv[0]);
exit(EXIT_FAILURE);
}
char *pathval = getenv("PATH");
if (pathval)
printf("the original PATH = %s\n", getenv("PATH"));
if (setenv("PATH", argv[1], 1) != 0)
{
fprintf(stderr, "Insufficient memory to add
a new variable to the environment");
exit(EXIT_FAILURE);
}
printf("after modify PATH = %s\n", getenv("PATH"));
return EXIT_SUCCESS;
}
修改一个现有的name,也要考虑两种情况
(a)如果新value的长度少于或等于现有value的长度
astrol@astrol:~/c/learn_by_example/test$ gdb getenv_test -q
Reading symbols from /home/astrol/c/learn_by_example/test/getenv_test...done.
(gdb) break main
Breakpoint 1 at 0x80484fd: file getenv_test.c, line 7.
(gdb) run $(perl -e 'print "A"x10')
Starting program: /home/astrol/c/learn_by_example/test/getenv_test $(perl -e 'print "A"x10')
Breakpoint 1, main (argc=2, argv=0xbffff6f4) at getenv_test.c:7
7 if (argc != 2)
(gdb) print/x environ
$1 = 0xbffff700
(gdb) print/x getenv("PATH")
$2 = 0xbffffe17
(gdb) x/32xw environ
0xbffff700: 0xbffff86b 0xbffff87b 0xbffff886 0xbffff8d7
0xbffff710: 0xbffff8f7 0xbffff90a 0xbffff916 0xbffffe06
0xbffff720: 0xbffffe12 0xbffffe6f 0xbffffe85 0xbffffe9d
0xbffff730: 0xbffffeac 0xbffffec3 0xbffffeec 0xbffffefd
0xbffff740: 0xbfffff06 0xbfffff18 0xbfffff20 0xbfffff32
0xbffff750: 0xbfffff41 0xbfffff74 0xbfffff89 0xbfffffa9
0xbffff760: 0x00000000 0x00000020 0x00130414 0x00000021
0xbffff770: 0x00130000 0x00000010 0x078bf3bf 0x00000006
(gdb) x/s 0xbffffe12
0xbffffe12: "PATH=/opt/skyeye/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
(gdb) next
13 char *pathval = getenv("PATH");
(gdb)
14 if (pathval)
(gdb)
15 printf("the original PATH = %s\n", getenv("PATH"));
(gdb)
the original PATH = /opt/skyeye/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
17 if (setenv("PATH", argv[1], 1) != 0)
(gdb)
22 printf("after modify PATH = %s\n", getenv("PATH"));
(gdb)
after modify PATH = AAAAAAAAAA
24 return EXIT_SUCCESS;
(gdb) x/32xw environ
0xbffff700: 0xbffff86b 0xbffff87b 0xbffff886 0xbffff8d7
0xbffff710: 0xbffff8f7 0xbffff90a 0xbffff916 0xbffffe06
0xbffff720: 0x0804b018 0xbffffe6f 0xbffffe85 0xbffffe9d
0xbffff730: 0xbffffeac 0xbffffec3 0xbffffeec 0xbffffefd
0xbffff740: 0xbfffff06 0xbfffff18 0xbfffff20 0xbfffff32
0xbffff750: 0xbfffff41 0xbfffff74 0xbfffff89 0xbfffffa9
0xbffff760: 0x00000000 0x00000020 0x00130414 0x00000021
0xbffff770: 0x00130000 0x00000010 0x078bf3bf 0x00000006
(gdb) x/s 0x0804b018
0x804b018: "PATH=AAAAAAAAAA"
(gdb)
run命令之后的perl -e ‘print “A”x10’用于产生连续的10个A,作为新的PATH值。
新的value比原来的value短,我们发现程序改动了environment list中相应的entry,使其指向新的value,而旧的的value还在原处,并没有更改,这点可以通过x命令查看:
(gdb) x/s 0xbffffe12
0xbffffe12: "PATH=/opt/skyeye/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
(gdb)
的确如此!
(b)新value比原来value长的情况,这次我们使用150个连续的A
(gdb) run $(perl -e 'print "A"x150')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/astrol/c/learn_by_example/test/getenv_test $(perl -e 'print "A"x150')
Breakpoint 1, main (argc=2, argv=0xbffff674) at getenv_test.c:7
7 if (argc != 2)
(gdb) print/x environ
$3 = 0xbffff680
(gdb) print/x getenv("PATH")
$4 = 0xbffffe17
(gdb) x/32xw environ
0xbffff680: 0xbffff86b 0xbffff87b 0xbffff886 0xbffff8d7
0xbffff690: 0xbffff8f7 0xbffff90a 0xbffff916 0xbffffe06
0xbffff6a0: 0xbffffe12 0xbffffe6f 0xbffffe85 0xbffffe9d
0xbffff6b0: 0xbffffeac 0xbffffec3 0xbffffeec 0xbffffefd
0xbffff6c0: 0xbfffff06 0xbfffff18 0xbfffff20 0xbfffff32
0xbffff6d0: 0xbfffff41 0xbfffff74 0xbfffff89 0xbfffffa9
0xbffff6e0: 0x00000000 0x00000020 0x00130414 0x00000021
0xbffff6f0: 0x00130000 0x00000010 0x078bf3bf 0x00000006
(gdb) next
13 char *pathval = getenv("PATH");
(gdb)
14 if (pathval)
(gdb)
15 printf("the original PATH = %s\n", getenv("PATH"));
(gdb)
the original PATH = /opt/skyeye/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
17 if (setenv("PATH", argv[1], 1) != 0)
(gdb)
22 printf("after modify PATH = %s\n", getenv("PATH"));
(gdb)
after modify PATH = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
24 return EXIT_SUCCESS;
(gdb) x/32xw environ
0xbffff680: 0xbffff86b 0xbffff87b 0xbffff886 0xbffff8d7
0xbffff690: 0xbffff8f7 0xbffff90a 0xbffff916 0xbffffe06
0xbffff6a0: 0x0804b018 0xbffffe6f 0xbffffe85 0xbffffe9d
0xbffff6b0: 0xbffffeac 0xbffffec3 0xbffffeec 0xbffffefd
0xbffff6c0: 0xbfffff06 0xbfffff18 0xbfffff20 0xbfffff32
0xbffff6d0: 0xbfffff41 0xbfffff74 0xbfffff89 0xbfffffa9
0xbffff6e0: 0x00000000 0x00000020 0x00130414 0x00000021
0xbffff6f0: 0x00130000 0x00000010 0x078bf3bf 0x00000006
(gdb) x/s 0x0804b018
0x804b018: "PATH=", 'A' <repeats 150 times>
(gdb) x/s 0xbffffe12
0xbffffe12: "PATH=/opt/skyeye/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
(gdb)
很容易发现,尽管此次新value比原来的value长,但程序的处理方式与之前一样,都是修改相应的environment list的entry。
接下来,我们再看看
astrol@astrol:~/source/glibc-2.16.0$ ps -aux | grep getenv
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
astrol 5824 0.0 3.6 29940 18376 pts/1 S+ 22:14 0:00 gdb getenv_test -q
astrol 5837 0.0 0.0 1972 244 pts/1 t 22:24 0:00 /home/astrol/c/learn_by_example/test/getenv_test AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
astrol 5841 0.0 0.1 5432 772 pts/0 S+ 22:28 0:00 grep --color=auto getenv
astrol@astrol:~/source/glibc-2.16.0$ cat /proc/5837/maps
00110000-0012e000 r-xp 00000000 08:01 136588 /lib/i386-linux-gnu/ld-2.13.so
0012e000-0012f000 r--p 0001d000 08:01 136588 /lib/i386-linux-gnu/ld-2.13.so
0012f000-00130000 rw-p 0001e000 08:01 136588 /lib/i386-linux-gnu/ld-2.13.so
00130000-00131000 r-xp 00000000 00:00 0 [vdso]
00131000-002ad000 r-xp 00000000 08:01 135998 /lib/i386-linux-gnu/libc-2.13.so
002ad000-002af000 r--p 0017c000 08:01 135998 /lib/i386-linux-gnu/libc-2.13.so
002af000-002b0000 rw-p 0017e000 08:01 135998 /lib/i386-linux-gnu/libc-2.13.so
002b0000-002b3000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 08:01 955995 /home/astrol/c/learn_by_example/test/getenv_test
08049000-0804a000 r--p 00000000 08:01 955995 /home/astrol/c/learn_by_example/test/getenv_test
0804a000-0804b000 rw-p 00001000 08:01 955995 /home/astrol/c/learn_by_example/test/getenv_test
0804b000-0806c000 rw-p 00000000 00:00 0 [heap]
b7fe9000-b7fea000 rw-p 00000000 00:00 0
b7ffd000-b8000000 rw-p 00000000 00:00 0
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
astrol@astrol:~/source/glibc-2.16.0$
注意到没,0x0804b000 ~ 0x0806c000是堆,可见我们输入的参数被存储在了堆上,这也验证了setenv函数会分配存储区的结论。
这种情况(也即修改一个现有的name)下,putenv函数会怎么做呢?
putenv函数所做的除了动态申请内存空间外,其余的与getenv函数完全相同,它直接将我们传递的字符串首地址作为entry覆盖原有的entry,这也是putenv与getenv最大的不同。
接着我们考察增加一个新的name的情况。我们给系统增加一个名叫ASTRO的新环境变量。
实例getenv_add.c如下
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
if (argc != 3) {
fprintf(stderr, "Usage:%s + name + value.\n", argv[0]);
exit(EXIT_FAILURE);
}
const char *name = argv[1];
const char *value = argv[2];
if (!getenv(name)) {
if (setenv(name, value, 1) != 0) {
fprintf(stderr, "Insufficient memory"
"to add a new variable to the environment.");
exit(EXIT_FAILURE);
}
char *astrol = getenv(name);
if (astrol)
printf("%s=%s\n",name, astrol);
} else {
fprintf(stderr, "ASTROL existed!\n");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
调试如下:
astrol@astrol:~/c/learn_by_example/test$ gdb getenv_add -q
Reading symbols from /home/astrol/c/learn_by_example/test/getenv_add...done.
(gdb) break main
Breakpoint 1 at 0x80484fd: file getenv_add.c, line 6.
(gdb) run ASTROL http://blog.csdn.net/astrotycoon
Starting program: /home/astrol/c/learn_by_example/test/getenv_add ASTROL http://blog.csdn.net/astrotycoon
Breakpoint 1, main (argc=3, argv=0xbffff6e4) at getenv_add.c:6
6 if (argc != 3) {
(gdb) print/x environ
$1 = 0xbffff6f4
(gdb) x/32xw environ
0xbffff6f4: 0xbffff86c 0xbffff87c 0xbffff887 0xbffff8d8
0xbffff704: 0xbffff8f8 0xbffff90b 0xbffff917 0xbffffe07
0xbffff714: 0xbffffe13 0xbffffe70 0xbffffe86 0xbffffe9e
0xbffff724: 0xbffffead 0xbffffec4 0xbffffeed 0xbffffefe
0xbffff734: 0xbfffff07 0xbfffff19 0xbfffff21 0xbfffff33
0xbffff744: 0xbfffff42 0xbfffff75 0xbfffff8a 0xbfffffaa
0xbffff754: 0x00000000 0x00000020 0x00130414 0x00000021
0xbffff764: 0x00130000 0x00000010 0x078bf3bf 0x00000006
(gdb) next
11 const char *name = argv[1];
(gdb)
12 const char *value = argv[2];
(gdb)
14 if (!getenv(name)) {
(gdb)
15 if (setenv(name, value, 1) != 0) {
(gdb)
20 char *astrol = getenv(name);
(gdb)
21 if (astrol)
(gdb)
22 printf("%s=%s\n",name, astrol);
(gdb)
ASTROL=http://blog.csdn.net/astrotycoon
29 return EXIT_SUCCESS;
(gdb) print/x environ
$2 = 0x804b008
(gdb) x/32xw environ
0x804b008: 0xbffff86c 0xbffff87c 0xbffff887 0xbffff8d8
0x804b018: 0xbffff8f8 0xbffff90b 0xbffff917 0xbffffe07
0x804b028: 0xbffffe13 0xbffffe70 0xbffffe86 0xbffffe9e
0x804b038: 0xbffffead 0xbffffec4 0xbffffeed 0xbffffefe
0x804b048: 0xbfffff07 0xbfffff19 0xbfffff21 0xbfffff33
0x804b058: 0xbfffff42 0xbfffff75 0xbfffff8a 0xbfffffaa
0x804b068: 0x0804b078 0x00000000 0x00000000 0x00000031
0x804b078: 0x52545341 0x683d4c4f 0x3a707474 0x6c622f2f
(gdb) x/s 0x0804b078
0x804b078: "ASTROL=http://blog.csdn.net/astrotycoon"
(gdb)
查看我们传入的参数地址
(gdb) print/x argv[1]
$6 = 0xbffff844
(gdb) x/s argv[1]
0xbffff844: "ASTROL"
(gdb) print/x argv[2]
$7 = 0xbffff84b
(gdb) x/s argv[2]
0xbffff84b: "http://blog.csdn.net/astrotycoon"
很明显,程序为我们传入的参数动态申请了内存空间。
这次变化最大的是我们发现environment list都被放到堆里来了。
如果我们继续增加一个新name,会怎么呢?
程序会调用realloc,以分配比原空间多存放一个指针的空间。
所以这一切,putenv的操作几乎与getenv是一样的,同样的会将environment list该放到堆里,唯一不同的是不会为参数动态申请内存空间。
最后,我们一起看看删除操作具体做了什么呢?实例unsetenv_test.c如下:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
if (getenv("PATH")) {
if (unsetenv("PATH") != 0) {
fprintf(stderr, "name is NULL,"
"points to a string of length 0, "
"or contains an '=' character.");
exit(EXIT_FAILURE);
}
} else {
fprintf(stderr, "name does not existed!\n");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
调试结果如下:
astrol@astrol:~/c/learn_by_example/test$ gdb unsetenv_test -q
Reading symbols from /home/astrol/c/learn_by_example/test/unsetenv_test...done.
(gdb) break main
Breakpoint 1 at 0x804849d: file unsetenv_test.c, line 6.
(gdb) run
Starting program: /home/astrol/c/learn_by_example/test/unsetenv_test
Breakpoint 1, main (argc=1, argv=0xbffff704) at unsetenv_test.c:6
6 if (getenv("PATH")) {
(gdb) print/x environ
$1 = 0xbffff70c
(gdb) print/x getenv("PATH")
$2 = 0xbffffe15
(gdb) x/32xw environ
0xbffff70c: 0xbffff869 0xbffff879 0xbffff884 0xbffff8d5
0xbffff71c: 0xbffff8f5 0xbffff908 0xbffff914 0xbffffe04
0xbffff72c: 0xbffffe10 0xbffffe6d 0xbffffe83 0xbffffe9b
0xbffff73c: 0xbffffeaa 0xbffffec1 0xbffffeea 0xbffffefb
0xbffff74c: 0xbfffff04 0xbfffff16 0xbfffff1e 0xbfffff30
0xbffff75c: 0xbfffff3f 0xbfffff72 0xbfffff87 0xbfffffa7
0xbffff76c: 0x00000000 0x00000020 0x00130414 0x00000021
0xbffff77c: 0x00130000 0x00000010 0x078bf3bf 0x00000006
(gdb) next
7 if (unsetenv("PATH") != 0) {
(gdb)
18 return EXIT_SUCCESS;
(gdb) x/32xw environ
0xbffff70c: 0xbffff869 0xbffff879 0xbffff884 0xbffff8d5
0xbffff71c: 0xbffff8f5 0xbffff908 0xbffff914 0xbffffe04
0xbffff72c: 0xbffffe6d 0xbffffe83 0xbffffe9b 0xbffffeaa
0xbffff73c: 0xbffffec1 0xbffffeea 0xbffffefb 0xbfffff04
0xbffff74c: 0xbfffff16 0xbfffff1e 0xbfffff30 0xbfffff3f
0xbffff75c: 0xbfffff72 0xbfffff87 0xbfffffa7 0x00000000
0xbffff76c: 0x00000000 0x00000020 0x00130414 0x00000021
0xbffff77c: 0x00130000 0x00000010 0x078bf3bf 0x00000006
(gdb)
可以看到很简单,只需在environment list中找到相应的entry,然后将后续的指针都向environment list首部顺序移动一个位置即可。
那么我们有没有想过,为什么会这样呢?或者说为什么要这样设计呢?
一般的,environment list(指向实际的name=value字符串的指针数组)和environment strings会被存放在进程存储空间的顶部(也即栈之上)。
删除一个环境变量很简单 —— 只要现在environment list中找到相应entry,然后将所有后续entry都向environment list首部顺序移动一个位置。
修改一个现有环境变量也很简单,只需更新修改相应entry,至于字符串是动态申请还是直接使用我们传递的字符串,会根据使用函数的不同而不同。
但是要是增加一个环境变量就困难的多了,为什么这么说呢?因为environment list和environment strings通常占用的是进程地址空间的顶部,所以它不能再向高地址方向(向上)扩展;同时也不能移动在它之下的个函数栈帧,所以它也不能向低地址方向(向下)扩展。综合这两点,可以得出结论:该空间的长度不能增加,所以要在堆中动态申请。
总结如下:
(1)如果修改一个现有的name
这种情况很好办,不需要移动enviroment list,不管是getenv函数还是putenv函数,都只需要修改environment list中相应的entry即可,使entry指向新的value。当然value会因为函数的不同而存储的内存的位置不同。
(2)如果要增加一个新的name,则操作会变得复杂一些。
(a)如果这是第一次增加一个新name,则必须调用malloc为新的environment list在堆中分配新空间。接着,将原来的environment list复制到新的environment list,并将指向新name=value字符串的entry存放在新environment list的尾部,然后又将一个空指针存放在其后。 最后使environ指向新的environment list。不过,新environment list中的大多数entry仍指向栈顶之上的name=value字符串。
(b)如果这不是第一次增加一个新name,则可知以前已调用malloc在堆中位environment list分配可空间,所以只要调用realloc,以分配比原空间多存放一个指针的空间。然后将指向新name=value字符串的指针存放在该表表尾,后面跟着一个空指针。
参考资料:
《UNIX环境高级编程》
《LINUX PROGRAMMING BY EXAMPLE》