Val*_*ron 38 c unix linux environment-variables setenv
我一直在考虑环境变量,并有一些问题/观察.
putenv(char *string);
这个电话似乎有致命的缺陷.因为它不复制传递的字符串,所以无法使用本地调用它,并且无法保证堆分配的字符串不会被覆盖或意外删除.此外(尽管我还没有测试过),因为环境变量的一个用途是将值传递给子环境,如果子进程调用其中一个exec*()函数,这似乎没用.我错了吗?
Linux手册页指出glibc 2.0-2.1.1放弃了上述行为并开始复制字符串,但这导致了glibc 2.1.2中修复的内存泄漏.我不清楚这个内存泄漏是什么或它是如何修复的.
setenv()复制字符串,但我不确切知道它是如何工作的.当进程加载但是它被修复时,将分配环境空间.这里有一些(任意?)惯例吗?例如,在env字符串指针数组中分配比当前使用的更多的插槽,并根据需要向下移动空终止指针?是否在环境本身的地址空间中分配了新的(复制的)字符串的内存,如果它太大而不适合您只需获得ENOMEM?
考虑到上述问题,有什么理由,更喜欢putenv()过setenv()?
Jon*_*ler 40
- []
putenv(char *string);]电话似乎有致命的缺陷.
是的,这是致命的缺陷. 它保存在POSIX(1988)中,因为那是现有技术.该 更正: POSIX 1990标准在§B.4.6.1中说"附加功能putenv ()和clearenv()被认为但被拒绝".1997年的单Unix规范(SUS)版本2列出setenv()机制后来到了.putenv()但不是setenv()或unsetenv().下一个版本(2004)做了定义都setenv()和unsetenv()为好.
因为它不复制传递的字符串,所以无法使用本地调用它,并且无法保证堆分配的字符串不会被覆盖或意外删除.
你是正确的,局部变量几乎总是一个不好的选择putenv()- 异常是模糊的,几乎不存在.如果字符串在堆上分配(使用malloc()等),则必须确保代码不会对其进行修改.如果是,它会同时修改环境.
此外(尽管我还没有测试过),因为环境变量的一个用途是将值传递给子环境,如果子进程调用其中一个
exec*()函数,这似乎没用.我错了吗?
这些exec*()函数创建环境的副本并将其传递给已执行的进程.那里没问题.
Linux手册页指出glibc 2.0-2.1.1放弃了上述行为并开始复制字符串,但这导致了glibc 2.1.2中修复的内存泄漏.我不清楚这个内存泄漏是什么或它是如何修复的.
内存泄漏的产生是因为一旦你putenv()使用字符串调用,就不能再将该字符串用于任何目的,因为你无法判断它是否仍在使用中,尽管你可以通过覆盖它来修改该值(如果你有不确定的结果)将名称更改为在环境中的其他位置找到的环境变量的名称.因此,如果您已分配空间,putenv()则如果再次更改变量,则经典会泄漏它.当putenv()开始复制数据时,分配的变量变得未引用,因为putenv()不再保留对参数的引用,但是用户期望环境将引用它,因此内存被泄露.我不确定修复是什么 - 我希望它能恢复原来的行为.
setenv()复制字符串,但我不确切知道它是如何工作的.当进程加载但是它被修复时,将分配环境空间.
原始环境空间是固定的; 当你开始修改它时,规则就会改变.即使这样putenv(),原始环境也会被修改,并且可能会因添加新变量而增加,或者由于更改现有变量而具有更长的值.
这里有一些(任意?)惯例吗?例如,在env字符串指针数组中分配比当前使用的更多的插槽,并根据需要向下移动空终止指针?
这就是setenv()机制可能会做的事情.(全局)变量environ指向环境变量指针数组的开头.如果它一次指向一个内存块而在不同时间指向一个不同的块,则切换环境,就像那样.
是否在环境本身的地址空间中分配了新的(复制的)字符串的内存,如果它太大而不适合您只需获得ENOMEM?
嗯,是的,你可以得到ENOMEM,但你必须努力.如果你的环境变得太大,你可能无法正确执行其他程序 - 环境将被截断或exec操作将失败.
考虑到上述问题,有没有理由更喜欢putenv()而不是setenv()?
setenv()在新代码中使用.setenv(),但不要将其作为最高优先级.putenv()在新代码中使用.阅读The Open Group Base Specifications Issue 6 中的手册页的RATIONALE部分setenv.
putenv并且setenv都应该符合POSIX标准.如果你有代码putenv,并且代码运行良好,请不要管它.如果您正在开发新代码,您可能需要考虑setenv.
看看glibc的源代码,如果你想看到的一个实现的示例setenv(stdlib/setenv.c)或putenv(stdlib/putenv.c).
没有特殊的"环境"空间 - setenv只是动态地为字符串分配空间(malloc例如),就像你通常那样.因为环境不包含其中它的每个字符串是从哪里来的任何迹象显示,这是不可能的setenv或unsetenv释放可能已经由以前的调用被动态地分配给SETENV任何空间.
"因为它不复制传递的字符串,所以不能用本地调用它,并且不能保证堆分配的字符串不会被覆盖或意外删除." putenv的目的是确保如果你有一个堆分配的字符串,就可以故意删除它.这就是理由文本的意思是"唯一可用于在不允许内存泄漏的情况下添加到环境中的函数".是的,您可以使用本地调用它,只需putenv("FOO=")从函数返回之前从环境(或unsetenv)中删除字符串.
关键是使用putenv使得从环境中删除字符串的过程完全具有确定性.setenv将在某些现有实现上修改环境中的现有字符串,如果新值更短(以避免总是泄漏内存),并且因为它在您调用setenv时创建了一个副本,则您无法控制最初动态分配的字符串因此,当它被移除时你无法释放它.
同时,setenv 本身(或unsetenv)无法释放前一个字符串,因为 - 甚至忽略putenv - 字符串可能来自原始环境,而不是由先前的setenv调用分配.
(这整个答案假设一个正确实现的putenv ,即不是你提到的glibc 2.0-2.1.1中的那个.)
| 归档时间: |
|
| 查看次数: |
23952 次 |
| 最近记录: |