是否有迭代 POSIX 环境变量的函数?

Is there a function for iterating through POSIX environment variables?

我刚刚读到有关 complexities of environ and in particular how thread-unsafe it is due to it being legal to assign to it 替换整个环境的信息。

请记住,是否有任何类型的 API 用于遍历所有当前环境变量而不直接使用静态 environ 数组?

明确地说,我知道 getenv(), setenv() and friends (the less said about putenv() 更好的 IMO)- 这些只允许恢复特定变量,而不是遍历所有变量。

我还编写了大量直接遍历 environ 的代码。但令我印象深刻的是,在任何明智的多线程应用程序中,使用环境的唯一明智的方法是尽早在 main() 中插入代码,将其插入 unordered_map 或类似的并且永远只从那里使用它;或者只是将其视为完全不可变的,并希望 none 的链接库不这样做。

所以我想知道是否有任何更安全的接口定义为 POSIX 的一部分,或者可能是特定于平台的,我不知道?

"So I was wondering if there was any safer interfaces defined as part of POSIX, or perhaps platform-specific, of which I was unaware?"

没有人知道。

"But it strikes me that in any sensible multi-threaded application, the only sane way to use the environment is either insert code as early as possible in main() to slurp it into an unordered_map or similar and only ever use it from there; or to just treat it as totally immutable and hope none of you linked libraries do otherwise."

这不会使任何事情更安全,因为如果从其他任何地方调用 setenv()putenv(),您将需要更新该地图(可能来自不受您控制的代码)。

在多线程应用程序中,使用环境的唯一明智方法是将其视为只读。如果你需要修改它,你应该在程序初始化阶段进行,在启动任何线程之前。

由于环境的目的是从环境到应用程序进行通信,因此将环境视为只读应该不是问题。与此用例一致,除了使用 environ 之外,没有用于迭代环境的标准接口,并且除非应用程序承诺不修改环境,否则不能在多线程应用程序中安全使用。

据我所知,没有任何标准库函数会修改环境(setenvputenv 除外),IMO 也没有理智的库会这样做。

认识到有时需要修改环境,以引入默认值或作为分叉子项初始化序列的一部分。在前一种情况下,通常可以在启动线程之前如上所述执行修改。

后一种情况在多线程应用程序中很棘手,但无论如何,仅在子进程中修改环境就足够容易了(即在调用 fork() 之后和调用 [=14 之前) =]。或者,可以构造一个全新的环境并将其提供给接受环境参数的 exec() 版本。

简而言之,使用环境作为全局变量的替代品比首先使用全局变量更糟糕(恕我直言)。但是,将它用于发明它的目的——由父进程配置子进程——应该不会造成问题。


在评论中进行了有趣的讨论之后,似乎值得添加一些注释。

首先,许多标准(和非标准)库函数读取环境。这对于调试特别方便,但它也用于一些配置选项,包括执行路径搜索、语言环境、控制台 window 大小、时区等等,等等(有一个很长但不完整的列在 Posix 标准的基本定义卷中。)

由于 getenv 不能在多线程代码中安全使用,除非知道不能对环境进行并发修改,因此禁止标准库函数修改环境(接口除外)似乎是合理的专为此类修改而设计)。据我所知,Posix 不包括此禁令,但它确实要求所有接口都记录其使用环境,而且我相信除了 putenv 之外我没有看到任何接口, setenvunsetenv 记录在案以修改环境。总的来说,我认为多线程代码发起线程后不修改环境的假设是完全合理的(甚至是必要的)。

当然,在启动线程之前修改单线程代码或多线程代码中的环境是合法的。但最佳实践规定只能使用两种可能的修改机制中的一种:

  1. setenv(和unsetenv)界面。
  2. 直接将 environ 分配给另一个数组,其中原始 environ 中存在的任何字符串都已复制到应用程序管理的内存中。

putenv 的使用确实不可取,但如果不与上述其他两种可能性混合使用是可以接受的。

同样,Posix 不提供限制、指南或建议(除了更喜欢 setenv 而不是 putenv),所以将以上作为我的建议。