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