关于 putenv() 和 setenv() 的问题 [英] Questions about putenv() and setenv()

查看:38
本文介绍了关于 putenv() 和 setenv() 的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在思考环境变量,并有一些问题/观察.

I have been thinking a little about environment variables and have a few questions/observations.

  • putenv(char *string);

这个调用似乎有致命的缺陷.因为它不复制传递的字符串,所以不能用本地调用它,并且不能保证堆分配的字符串不会被覆盖或意外删除.此外(虽然我没有测试过),因为环境变量的一种用途是将值传递给孩子的环境,如果孩子调用 exec*() 函数之一,这似乎没用.我错了吗?

This call seems fatally flawed. Because it doesn't copy the passed string you can't call it with a local and there is no guarantee a heap allocated string won't be overwritten or accidentally deleted. Furthermore (though I haven't tested it), since one use of environment variables is to pass values to child's environment this seems useless if the child calls one of the exec*() functions. Am I wrong in that?

Linux 手册页指出 glibc 2.0-2.1.1 放弃了上述行为并开始复制字符串,但这导致了内存泄漏,该问题已在 glibc 2.1.2 中修复.我不清楚这个内存泄漏是什么或它是如何修复的.

The Linux man page indicates that glibc 2.0-2.1.1 abandoned the above behavior and began copying the string but this led to a memory leak that was fixed in glibc 2.1.2. It's not clear to me what this memory leak was or how it was fixed.

setenv() 复制字符串,但我不知道它是如何工作的.环境空间在进程加载时分配,但它是固定的.这里有一些(任意的?)约定吗?例如,在 env 字符串指针数组中分配比当前使用更多的插槽并根据需要向下移动空终止指针?新(复制的)字符串的内存是否在环境本身的地址空间中分配,如果它太大而无法容纳,您只需获取 ENOMEM?

setenv() copies the string but I don't know exactly how that works. Space for the environment is allocated when the process loads but it is fixed. Is there some (arbitrary?) convention at work here? For example, allocating more slots in the env string pointer array than currently used and moving the null terminating pointer down as needed? Is the memory for the new (copied) string allocated in the address space of the environment itself and if it is too big to fit you just get ENOMEM?

考虑到上述问题,是否有任何理由更喜欢 putenv() 而不是 setenv()?

Considering the above issues, is there any reason to prefer putenv() over setenv()?

推荐答案

  • [The] putenv(char *string); [...] 调用似乎存在致命缺陷.
  • [The] putenv(char *string); [...] call seems fatally flawed.

是的,它有致命的缺陷.它被保留在 POSIX (1988) 中,因为那是现有技术.setenv() 机制稍后出现. 更正: POSIX 1990 标准在 §B.4.6.1 中说附加函数 putenv()clearenv() 被考虑但被拒绝".1997 年的 单一 Unix 规范 (SUS) 版本 2 列出了 putenv() 但不是 setenv()unsetenv().下一个修订版(2004 年)确实定义了 setenv()unsetenv()

Yes, it is fatally flawed. It was preserved in POSIX (1988) because that was the prior art. The setenv() mechanism arrived later. Correction: The POSIX 1990 standard says in §B.4.6.1 "Additional functions putenv() and clearenv() were considered but rejected". The Single Unix Specification (SUS) version 2 from 1997 lists putenv() but not setenv() or unsetenv(). The next revision (2004) did define both setenv() and unsetenv() as well.

因为它不复制传递的字符串,所以不能用本地调用它,并且不能保证堆分配的字符串不会被覆盖或意外删除.

Because it doesn't copy the passed string you can't call it with a local and there is no guarantee a heap allocated string won't be overwritten or accidentally deleted.

您是对的,局部变量几乎总是传递给 putenv() 的错误选择——异常模糊到几乎不存在的程度.如果字符串分配在堆上(使用 malloc() 等),您必须确保您的代码不会修改它.如果是,则同时在修改环境.

You're correct that a local variable is almost invariably a bad choice to pass to putenv() — the exceptions are obscure to the point of almost not existing. If the string is allocated on the heap (with malloc() et al), you must ensure that your code does not modify it. If it does, it is modifying the environment at the same time.

此外(虽然我还没有测试过),因为环境变量的一种用途是将值传递给孩子的环境,如果孩子调用 exec*() 函数之一,这似乎没用.我错了吗?

Furthermore (though I haven't tested it), since one use of environment variables is to pass values to child's environment this seems useless if the child calls one of the exec*() functions. Am I wrong in that?

exec*() 函数复制环境并将其传递给执行的进程.那里没有问题.

The exec*() functions make a copy of the environment and pass that to the executed process. There's no problem there.

Linux 手册页表明 glibc 2.0-2.1.1 放弃了上述行为并开始复制字符串,但这导致了内存泄漏,该问题已在 glibc 2.1.2 中修复.我不清楚这个内存泄漏是什么或它是如何修复的.

The Linux man page indicates that glibc 2.0-2.1.1 abandoned the above behavior and began copying the string but this led to a memory leak that was fixed in glibc 2.1.2. It's not clear to me what this memory leak was or how it was fixed.

内存泄漏的产生是因为一旦你用一个字符串调用了 putenv() ,你就不能为了任何目的再次使用该字符串,因为你无法判断它是否仍在使用中,尽管你可以通过覆盖来修改该值(如果将名称更改为在环境中另一个位置找到的环境变量的名称,则结果不确定).所以,如果你已经分配了空间,如果你再次改变变量,经典的 putenv() 会泄漏它.当 putenv() 开始复制数据时,分配的变量变为未引用,因为 putenv() 不再保留对参数的引用,但用户期望环境将引用它,所以内存泄漏了.我不确定修复是什么 - 我有 3/4 的期望它会恢复到旧的行为.

The memory leak arises because once you have called putenv() with a string, you cannot use that string again for any purpose because you can't tell whether it is still in use, though you could modify the value by overwriting it (with indeterminate results if you change the name to that of an environment variable found at another position in the environment). So, if you have allocated space, the classic putenv() leaks it if you change the variable again. When putenv() began to copy data, allocated variables became unreferenced because putenv() no longer kept a reference to the argument, but the user expected that the environment would be referencing it, so the memory was leaked. I'm not sure what the fix was — I would 3/4 expect it was to revert to the old behaviour.

setenv() 复制字符串,但我不知道它是如何工作的.环境空间在进程加载时分配,但它是固定的.

setenv() copies the string but I don't know exactly how that works. Space for the environment is allocated when the process loads but it is fixed.

原环境空间固定;当你开始修改它时,规则就会改变.即使使用 putenv(),原始环境也会被修改,并且可能会因添加新变量或将现有变量更改为具有更长值而增长.

The original environment space is fixed; when you start modifying it, the rules change. Even with putenv(), the original environment is modified and could grow as a result of adding new variables, or as a result of changing existing variables to have longer values.

这里是否有一些(任意的?)约定在起作用?例如,在 env 字符串指针数组中分配比当前使用更多的插槽并根据需要向下移动空终止指针?

Is there some (arbitrary?) convention at work here? For example, allocating more slots in the env string pointer array than currently used and moving the null terminating pointer down as needed?

这就是 setenv() 机制可能会做的事情.(全局)变量 environ 指向指向环境变量的指针数组的开头.如果它一次指向一个内存块,在不同时间指向一个不同的块,那么环境就切换了,就这样.

That is what the setenv() mechanism is likely to do. The (global) variable environ points to the start of the array of pointers to environment variables. If it points to one block of memory at one time and a different block at a different time, then the environment is switched, just like that.

新(复制的)字符串的内存是否在环境本身的地址空间中分配,如果它太大而无法容纳,您只需获得 ENOMEM?

Is the memory for the new (copied) string allocated in the address space of the environment itself and if it is too big to fit you just get ENOMEM?

嗯,是的,您可以获得 ENOMEM,但您必须非常努力.并且如果你的环境增长得太大,你可能无法正常执行其他程序——要么环境被截断,要么执行操作失败.

Well, yes, you could get ENOMEM, but you'd have to be trying pretty hard. And if you grow the environment too large, you may be unable to exec other programs properly - either the environment will be truncated or the exec operation will fail.

考虑到上述问题,是否有任何理由更喜欢 putenv() 而不是 setenv()?

Considering the above issues, is there any reason to prefer putenv() over setenv()?

  • 在新代码中使用 setenv().
  • 更新旧代码以使用 setenv(),但不要将其作为首要任务.
  • 不要在新代码中使用 putenv().
    • Use setenv() in new code.
    • Update old code to use setenv(), but don't make it a top priority.
    • Do not use putenv() in new code.
    • 这篇关于关于 putenv() 和 setenv() 的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆