我一直在思考环境变量,并有一些问题/观察。
putenv(char *string);
此呼叫似乎存在致命缺陷。因为它不会复制传递的字符串,所以您不能使用本地调用它,并且不能保证分配给堆的字符串不会被覆盖或意外删除。此外(尽管我尚未测试过),因为环境变量的一种用法是将值传递给孩子的环境,如果孩子调用其中一个exec*()功能,这似乎毫无用处。我说错了吗?
exec*()
Linux手册页指示glibc 2.0-2.1.1放弃了上述行为,并开始复制字符串,但这导致内存泄漏,该问题已在glibc 2.1.2中修复。我尚不清楚此内存泄漏是什么或如何解决的。
setenv()复制字符串,但我不知道它是如何工作的。进程加载时为环境分配了空间,但它是固定的。这里有一些(任意的)约定吗?例如,在env字符串指针数组中分配的插槽比当前使用的更多,并根据需要向下移动null终止指针?是否在环境本身的地址空间中分配了新的(复制的)字符串的内存,如果太大而无法容纳,您只需要获取ENOMEM?
setenv()
考虑到上述问题,有什么理由,更喜欢putenv()过setenv()?
putenv()
[ putenv(char *string);…]呼叫似乎存在致命缺陷。
是的,它存在致命缺陷。 它被保留在POSIX(1988)中,因为那是现有技术。 该setenv()机制稍后到达。 更正: POSIX 1990标准在§B.4.6.1中说:“ 已考虑但拒绝了附加函数putenv () 和 clearenv() ”。该单一Unix规格(SUS)第2版自1997年名单putenv(),但没有setenv()或unsetenv()。下次修订版(2004)确实同时定义了setenv()和unsetenv()。
unsetenv()
因为它不会复制传递的字符串,所以您不能使用本地调用它,并且不能保证分配给堆的字符串不会被覆盖或意外删除。
您是正确的,几乎总是将局部变量传递给它是一个错误的选择putenv()-异常情况几乎不存在,这是很难理解的。如果字符串是在堆上分配的(使用malloc()et等),则必须确保代码不会对其进行修改。如果是这样,它将同时修改环境。
malloc()
此外(尽管我尚未测试过),因为环境变量的一种用法是将值传递给孩子的环境,如果孩子调用其中一个exec*()功能,这似乎毫无用处。我说错了吗?
这些exec*()函数复制环境并将其传递给执行的进程。那里没有问题。
之所以会发生内存泄漏,是因为一旦调用putenv()了字符串,就无法再将该字符串用于任何用途,因为您无法确定该字符串是否仍在使用中,尽管可以通过覆盖它来修改该值(如果不确定,则结果不确定)。将名称更改为在环境中其他位置找到的环境变量的名称)。因此,如果您已分配空间,则经典putenv()变量会在您再次更改变量时泄漏。当putenv()开始复制数据时,已分配的变量变为未引用状态,因为putenv()不再保留对参数的引用,但用户希望环境将对其进行引用,因此内存被泄漏。我不确定解决方案是什么- 我会3/4期望它会恢复到旧的行为。
setenv()复制字符串,但我不知道它是如何工作的。进程加载时为环境分配了空间,但它是固定的。
原始环境空间是固定的;当您开始修改它时,规则会更改。即使使用putenv(),原始环境也会被修改,并且可能会由于添加新变量或由于将现有变量更改为更长的值而增长。
这里有一些(任意的)约定吗?例如,在env字符串指针数组中分配的插槽比当前使用的更多,并根据需要向下移动null终止指针?
这就是该setenv()机制可能要做的。(全局)变量environ指向环境变量的指针数组的开始。如果它一次指向一个内存块,而在另一时间指向另一个内存块,则将切换环境,就像这样。
environ
是否在环境本身的地址空间中分配了新的(复制的)字符串的内存,如果太大而无法容纳,您只需要获取ENOMEM?
好吧,是的,您可以获得ENOMEM,但是您必须非常努力。而且,如果环境变得太大,则可能无法正确执行其他程序-环境将被截断或exec操作将失败。
考虑到上述问题,有什么理由比setenv()更喜欢putenv()吗?