c - 关于 putenv() 和 setenv() 的问题

我一直在思考环境变量,并有一些问题/意见。

  • putenv(char *string);

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

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

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

  • 考虑到上述问题,有什么理由更喜欢 putenv() 而不是 setenv()

最佳答案

  • [The] putenv(char *string); [...] call seems fatally flawed.

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

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() 等),您必须确保您的代码不会修改它。如果是,则同时在修改环境。

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 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 预计它会恢复到旧的行为。

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(),原始环境也会被修改,并且可能会因添加新变量或将现有变量更改为更长的值而增长。

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 指向环境变量指针数组的开头。如果它一次指向一个内存块,另一个时间指向另一个 block ,那么环境就被切换了,就这样。

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,但您必须非常努力。如果你将环境扩展得太大,你可能无法正确执行其他程序——要么环境被截断,要么执行操作失败。

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

  • 在新代码中使用 setenv()
  • 更新旧代码以使用 setenv(),但不要将其作为首要任务。
  • 不要在新代码中使用 putenv()

https://stackoverflow.com/questions/5873029/

相关文章:

python - 如何关闭被杀死的程序打开的套接字?

linux - 跨多台计算机管理用户配置文件

c - 为什么在 Linux 中使用 select

linux - 在家学习内核黑客和嵌入式开发?

linux - 使用 Linux 加载程序时 ldconfig 错误 :"is not a symb

linux - cron 如何在内部安排作业?

linux - qstat 和长作业名称

c - Linux内核代码中的 "EXPORT_SYMBOL"是什么意思?

linux - 应用程序如何在运行时解析为不同版本的共享库?

python - 带有隐藏窗口的跨平台子进程