【问题标题】:How to deal with getaddrinfo and thread-safety?如何处理 getaddrinfo 和线程安全?
【发布时间】:2013-09-04 00:40:17
【问题描述】:

我正在将 getaddrinfo 用于与 IPv6 相关的 C 项目。我电脑上的“man getaddrinfo”(uname -a: 3.5.0-23)只表示它是“可重入的”。所以我猜它不是线程安全的。

在需要线程安全的场景下,如何处理?我还检查了 UNP,但似乎没有提供具体答案。非常感谢。

【问题讨论】:

  • 我在写我自己的——据我所知,所有标准的 DNS 解析器都很糟糕。不过,该协议有点烦人:(
  • 你对可重入和线程安全函数的理解是什么?
  • @Steve-o,从那个 wiki 看来这两个概念不相关,也不暗示另一个。
  • 在这种情况下,“可重入”意味着“线程安全”,这就是为什么我们有像strtok_rlocaltime_r 这样的函数

标签: sockets ipv6 getaddrinfo


【解决方案1】:

getaddrinfo() 确实是线程安全的。这是RFC 3493 Section 6.1 要求的:

函数 getaddrinfo() 和 freeaddrinfo() 必须是线程安全的。

在某些平台上,gethostbyname() 是线程安全的,但在其他平台上则不是。 gethostbyname() 并非在所有平台上都是可重入的。如果您调用gethostbyname(),然后在同一线程中再次调用gethostbyname(),则第一次调用的数据将被第二次调用的数据覆盖。这是因为gethostbyname() 通常在内部使用静态缓冲区,这就是为什么您必须在再次调用gethostbyname() 之前复制数据。 getaddrinfo() 不会遇到这个问题,因为每次调用它都会分配一个新的 addrinfo 结构。

【讨论】:

  • 嘿,你知道 Linux 和/或 OS X 是否有线程安全的getaddrinfo()吗?
  • @KristianSpangsege 这在我的回答中的前两句话中涵盖。
  • 好吧,只要那些系统采用了该 RFC,对吧?你具体知道他们有吗?
  • 无论如何,我在其他地方发现了它在 Linux 上确实是线程安全的。仅提及这一点是为了避免散布谣言。
【解决方案2】:

好吧,getaddrinfo 在某些平台上不是线程安全的,例如 Linux:http://man7.org/linux/man-pages/man3/getaddrinfo.3.html

   ┌────────────────┬───────────────┬────────────────────┐
   │Interface       │ Attribute     │ Value              │
   ├────────────────┼───────────────┼────────────────────┤
   │getaddrinfo()   │ Thread safety │ MT-Safe env locale │
   ├────────────────┼───────────────┼────────────────────┤
   │freeaddrinfo(), │ Thread safety │ MT-Safe            │
   │gai_strerror()  │               │                    │
   └────────────────┴───────────────┴────────────────────┘

注意环境和语言环境属性:

Other safety remarks
   Additional keywords may be attached to functions, indicating features
   that do not make a function unsafe to call, but that may need to be
   taken into account in certain classes of programs:

   locale Functions annotated with locale as an MT-Safety issue read
          from the locale object without any form of synchronization.
          Functions annotated with locale called concurrently with
          locale changes may behave in ways that do not correspond to
          any of the locales active during their execution, but an
          unpredictable mix thereof.

          We do not mark these functions as MT-Unsafe, however, because
          functions that modify the locale object are marked with
          const:locale and regarded as unsafe.  Being unsafe, the latter
          are not to be called when multiple threads are running or
          asynchronous signals are enabled, and so the locale can be
          considered effectively constant in these contexts, which makes
          the former safe.

   env    Functions marked with env as an MT-Safety issue access the
          environment with getenv(3) or similar, without any guards to
          ensure safety in the presence of concurrent modifications.

          We do not mark these functions as MT-Unsafe, however, because
          functions that modify the environment are all marked with
          const:env and regarded as unsafe.  Being unsafe, the latter
          are not to be called when multiple threads are running or
          asynchronous signals are enabled, and so the environment can
          be considered effectively constant in these contexts, which
          makes the former safe.

因此,如果不考虑它,您将得到随机的段错误。有关详细信息,请参阅此旧 glibc 错误讨论:https://sourceware.org/bugzilla/show_bug.cgi?id=13271

【讨论】:

    【解决方案3】:

    getaddrinfo() 是 POSIX 标准的一部分,而 POSIX 标准要求:

    freeaddrinfo() 和 getaddrinfo() 函数应该是线程安全的。

    来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html

    如果不是这样,操作系统可能不会声称符合 POSIX。

    您可能听说过的符合 POSIX 的正式操作系统:
    AIX、BSD、IRIX、macOS、(Open)Solaris、QNX 以及其他几个。
    在这些平台上,您可以依赖 getaddrinfo() 是线程安全的。

    众所周知的操作系统未正式符合 POSIX,但始终试图尽可能接近 POSIX 标准以实现软件兼容性:
    BeOS、FreeBSD、GNU、iOS、Linux、NetBSD、OpenBSD 以及其他几个。
    在这些平台上,您不能依赖 getaddrinfo() 完全是线程安全的,但您当然可以期望它足够线程安全,以便您可以在应用程序中的多个线程中使用它,而无需对其进行任何锁定。

    请注意,getaddrinfo() 在 Linux 上也是线程安全的,因为只有当您的代码在多个线程运行时更改了语言环境或环境并且这样做本身被视为线程不安全时,它才会成为线程不安全的。因此,如果您做了无论如何都被禁止的事情,您只能使getaddrinfo() 线程不安全(好吧,这并不是真正被禁止的,但您这样做需要您自担风险,因为这样做不安全)。

    还请注意,即使手册页不会这么说(一些 POSIX 手册页没有提到线程安全),但 POSIX 标准实际上要求:

    3.407 线程安全

    线程安全函数可以与其他函数同时安全地调用 调用相同的函数,或调用任何其他线程安全的 功能,由多个线程。 系统中定义的每个功能 除非明确说明,POSIX.1-2017 的接口卷是线程安全的 另有说明。 示例是任何“纯”函数,即 在访问静态存储或对象时保持互斥锁锁定 在线程之间共享。

    来源:http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html

    【讨论】:

      【解决方案4】:

      getaddrinfo() 是线程安全的。所有(或几乎所有)函数手册页都有关于线程安全的信息。 Reentrant 表示线程安全。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-26
        相关资源
        最近更新 更多