我刚刚阅读了complexities of environ
,特别是它是如何线程不安全的,因为assign to it替换整个环境是合法的。
请注意,是否有任何类型的API用于迭代所有当前不直接使用静态environ
数组的环境变量?
要明确的是,我知道getenv()
,setenv()
和朋友(关于putenv()
越少说越好的IMO) - 这些只允许恢复特定变量,而不是迭代通过他们所有。
我还编写了大量直接遍历environ
的代码。但令我印象深刻的是,在任何明智的多线程应用程序中,使用环境的唯一理智方法是尽可能早地在main()
中插入代码以将其插入到unordered_map
或类似的内容中从那里使用它;或者只是将它视为完全不可变的,并希望你们没有任何链接库能够做到这一点。
所以我想知道是否有更安全的接口被定义为POSIX的一部分,或者可能是平台特定的,我不知道?
答案 0 :(得分:3)
在多线程应用程序中,使用环境的唯一合理方法是将其视为只读。如果你需要修改它,你应该在程序初始化阶段,在启动任何线程之前这样做。
由于环境的目的是从环境到应用程序进行通信,因此将环境视为只读应该不是问题。与此用例一致,除了使用environ
之外,没有用于迭代环境的标准接口,并且除非应用程序承诺不修改环境,否则不能在多线程应用程序中安全地使用它。
据我所知,没有标准的库函数可以修改环境(setenv
和putenv
除外),IMO没有理智的库也会这样做。
人们认识到,有时需要修改环境,要么引入默认值,要么作为分叉子项的初始化序列的一部分。在前一种情况下,通常可以在启动线程之前按上述方式执行修改。
后一种情况在多线程应用程序中很棘手,但无论如何,仅在子代中修改环境就足够了(即在调用fork()
之后和调用exec*()
之前或者,可以构造一个全新的环境并将其提供给接受环境参数的exec()
版本。
简而言之,使用环境作为全局变量的替代,比首先使用全局变量更糟糕(imho)。但是,将它用于发明目的 - 由父母配置子进程 - 不应该导致问题。
在评论中进行了有趣的讨论之后,似乎值得添加几个笔记。
首先,许多标准(和非标准)库函数读取环境。这对于调试来说特别方便,但它也用于许多配置选项,包括执行路径搜索,区域设置,控制台窗口大小,时区等等。(有' sa Posix标准的基本定义量中的长但不完整的列表。)
由于getenv
无法在多线程代码中安全使用,除非已知不能对环境进行并发修改,因此禁止标准库函数修改环境似乎是合理的(除了为这样的修改)。据我所知,Posix不包括此禁令,但它确实要求所有界面记录他们对环境的使用,我不相信我已经看到putenv
以外的任何界面,记录了setenv
和unsetenv
以修改环境。总的来说,我认为假设在线程启动后不会在多线程代码中修改环境是完全合理的(甚至是必要的)。
当然,在启动线程之前,在单线程代码或多线程代码中修改环境是合法的。但是最佳实践要求只使用两种可能的修改机制中的一种:
setenv
(和unsetenv
)界面。environ
直接分配给其他数组,其中原始environ
中存在的任何字符串都已复制到应用程序管理的内存中。 putenv
的使用实际上并不可取,但如果不与上述其他两种可能性混合使用,则可以接受。
同样,Posix不提供限制,指导或建议(除了优先setenv
至putenv
之外),因此请将上述内容作为我的建议。
答案 1 :(得分:1)
"所以我想知道是否有任何更安全的接口被定义为POSIX的一部分,或者可能是平台特定的,我不知道?"
没有人知道。
"但令我印象深刻的是,在任何合理的多线程应用程序中,使用环境的唯一理智方法是尽早在
main()
中插入代码以将其插入到unordered_map
或类似的,只能从那里使用它;或者只是将其视为完全不可变的,希望你们没有任何链接库能够做到这一点。"
这不会让任何更安全的,因为如果从其他任何地方调用setenv()
或putenv()
(可能来自代码),您需要更新该地图不在你的控制之下)。