go - 为什么 CGO_ENABLE 会对虚拟内存产生如此大的影响?

标签 go

我有一个用 Golang 编写的小守护进程,它循环工作并做一些事情。我发现,在使用 CGO_ENABLE=1 或 CGO_ENABLED=0 编译时,守护进程的行为有所不同。例如,在 CGO_ENABLE=1(默认值)的情况下,程序的 VSZ 在短时间内(一小时内)膨胀到 1-2GB。当 CGO_ENABLED=0 时,VSZ 在很长一段时间内(数天)是相同的。看看下面的数字:

CGO_ENABLED=1(守护进程已经工作了 5 分钟)

$ grep -E 'VmSize|VmRSS' /proc/14916/status
VmSize:    1084052 kB
VmRSS:       12524 kB

CGO_ENABLED=0(守护进程已工作约 30 小时)

$ grep -E 'VmSize|VmRSS' /proc/15160/status
VmSize:    110232 kB
VmRSS:       9756 kB

守护进程不使用依赖于CGO的包或函数。其他 Go 语言编写的程序表现出相同的行为。我知道 VSZ 和 RSS 之间的区别,我很感兴趣这种行为的本质是什么?为什么用 CGO_ENABLED=1 编译的程序要求内核提供这么多内存?

我更喜欢不是“别担心,VSZ 只是一个虚拟内存,实际上它不被进程使用”形式的答案。

最佳答案

我可以做出有根据的猜测。

您可能知道,“引用”Go 实现的编译器(历史上称为“gc”;那个,可从 the main site 下载)默认生成静态链接的二进制文件。这意味着,此类二进制文件仅依赖于操作系统内核提供的所谓“系统调用”,而不依赖于操作系统(或第 3 方)提供的任何共享库。

在基于 Linux 的平台上,这并不完全正确:在默认设置(为 Linux 构建 Linux,即不交叉编译)中,生成的二进制文件实际上与 libc 链接 并使用 libpthread(间接地,通过 libc)。

这种“扭曲”源于 Go 标准库必须与操作系统交互的两个需求:

  1. DNS 解析,net 包需要它。
  2. os 包所需的用户和组查找。

这里的问题有两个:

  • Linux 本身(即内核,而不是整个操作系统)不提供任何方法来执行这些任务。

  • 自古以来,任何典型的类 UNIX 系统都使用称为“NSS”的特殊工具来完成这两项任务, 这是“名称服务开关”¹。

    NSS 提供可插拔模块,可以服务于 作为提供特定类型查询的数据库:DNS、用户/组数据库等(例如“服务”的知名名称等)。一个据称相当常见的例子 用户/组数据库的非标准提供者是本地的 联系 LDAP 服务器的服务。

在典型的基于 GNU/Linux 的操作系统上,NSS 是通过以下方式实现的 libc(在不太典型的系统上,它可能由 单独的共享库,但这并没有太大变化。

因为——同样,通常——libc 是一个相当稳定的 库在其 API 方面(它甚至提供版本化符号 为了面向 future ),Go 作者正确地决定链接 libc 以导入最小的符号子集(主要是 getaddrinfogetnameinfogetpwnam_r 等)就可以了 默认情况下完成,因为它在 99% 的情况下都是安全的, 如果不是,那些必须处理这些案件的人通常 无论如何都知道该怎么做。

因此,默认情况下 cgo 已启用并用于使用 NSS 实现这些查找。

如果 cgo 被禁用,Go 编译器会链接到它自己的 试图模仿一个子集的后备实现 完整的 NSS 实现(即解析 /etc/resolv.conf 并使用其中的信息直接查询此处列出的 DNS 服务器;解析 /etc/passwd/etc/group 为用户/组数据库查询提供服务。

如您所见,在默认情况下,

  • libc 被映射,并且
  • 它已初始化并使用一些内存来满足自己的需要 — 例如明显缓存 NSS 调用返回的数据。

相反,在禁用cgo 的情况下,上述两件事不会发生。您有更多静态链接的 stdlib 代码,但看起来默认情况在整体累积 RSS 使用方面仅胜过后者。

考虑研究输出 this query 为了额外的乐趣 ;-)


¹ 不要与 Mozilla 的 libnss 混淆.

关于go - 为什么 CGO_ENABLE 会对虚拟内存产生如此大的影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54456100/

相关文章:

docker - 从 Go api 连接到 Redshift 时出现 SSL 错误

database - 如何发起 HTTP 请求从 ClickHouse 数据库中获取数据

go - Ginkgo 到 Cobertura 和 JUnit

go - 如何在链表的给定索引处插入节点

regex in go lang匹配句号and - and A-z 0-9

go - 使用 golang 进行 NTP 检测,有效负载为空

go - 如何以编程方式确定 pod 是否处于 crashloopbackoff 状态

xml - 在 go 中解码 XML 时出现问题

json - 如何在 golang 中轻松编辑 JSON 类型(如 Node.js)

戈朗 : what assembly instructions are available