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

2024-01-01

我有一个用 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”;可以从主站点 https://golang.org) 默认情况下生成静态链接的二进制文件。这意味着,此类二进制文件仅依赖于操作系统内核提供的所谓“系统调用”,而不依赖于操作系统(或第三方)提供的任何共享库。

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

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

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

这里的问题有两个:

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

  • 任何典型的类 UNIX 系统永远都使用称为“NSS”的特殊工具来提供这两项任务, 这是“名称服务开关”。

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

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

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

所以,默认情况下cgo已启用and used使用 NSS 来实现这些查找。

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

正如你所看到的,在默认情况下,

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

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

考虑研究输出这个查询 https://www.google.com/search?q=CGO_ENABLED+site%3Ahttps%3A%2F%2Fgolang.org%2Fdoc为了额外的乐趣;-)


切勿与 Mozilla 的混淆libnss https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么CGO_ENABLE会对虚拟内存产生如此大的影响? 的相关文章

  • 解组转义 XML

    在 Go 中 我将如何解码此 XML 响应 我尝试过建立一个自定义UnMarshal方法在我的Answerstruct 但我运气不太好
  • 如何在golang中创建一个充满“000000...”数据的10MB文件?

    我打算在日志或磁盘队列等系统中使用 fdatasync 首先是在 ext4 等文件系统中创建一个带有 000000 的 10MB 文件 但我不知道如何正确地做到这一点 jnml fsc r630 src tmp SO 16797380 ls
  • 如何使信号量超时

    Go 中的信号量是通过通道来实现的 一个例子是这样的 https sites google com site gopatterns concurrency semaphores https sites google com site gop
  • 如何在golang中解析JSON而不需要解组两次

    我有一个 Web 套接字连接 它在 JSON 对象中发送不同类型的消息 并且我想将内容解组到一些已知的结构中 为此 我认为我应该执行以下操作 步骤 1 将 JSON 解组为通用映射 字符串 接口 步骤 2 找到我要找的钥匙 步骤 3 尝试将
  • 如何关闭 gorm 1.20.0 中的数据库实例

    由于我没有在 Close 函数中找到 gorm 实例 任何帮助将不胜感激 dbURI fmt Sprintf user s password s dbname s port s sslmode s TimeZone s username p
  • select 语句是否保证通道选择的顺序?

    继从这个答案 https stackoverflow com a 25795236 274460 如果一个 goroutine 在两个通道上进行选择 是否保证通道的选择顺序与其发送的顺序相同 我对发送者是单线程的情况特别感兴趣 例如 是否保
  • 如何为所有 API 端点全局设置 http.ResponseWriter Content-Type 标头?

    我是 Go 新手 现在正在用它构建一个简单的 API package main import encoding json fmt github com gorilla mux github com gorilla handlers log
  • 与通道相比,sync.WaitGroup 的优势是什么?

    我正在开发一个并发 Go 库 我偶然发现了 goroutine 之间两种不同的同步模式 其结果相似 等待组 https play golang org p ZYPLlcp16TZ package main import fmt sync t
  • 为什么 gmail API 以纯文本形式发送 html 电子邮件?

    我正在尝试使用 gmail API 发送 html 电子邮件 但由于某些原因 它会随机以纯文本 文本形式发送电子邮件 谷歌似乎改变了我设置的内容类型标头 这有什么理由吗 电子邮件内容始终完全相同 正如我测试的那样 API 仍处于实验阶段吗
  • 解压文件的简单方法

    有没有一种简单的方法可以用 Go 解压文件 现在我的代码是 func Unzip src dest string error r err zip OpenReader src if err nil return err defer r Cl
  • golang无法启动调试,显示未找到框架CoreFoundation

    我使用的是 Macbook pro m1 在数据迁移或更新到 monterey 后 不确定是什么原因导致的 golang 似乎无法调试或 go list 编译 我尝试重新安装 更新 golang 结果是一样的 Go版本 1 17 3 dar
  • golang.org 包和标准库之间的区别

    我使用 go 已经有一段时间了 我注意到 Go 标准库 和 golang org x 之间存在重复的包 我的问题是 为什么它们被释放两次 在这两者中 我应该使用哪一个 更新的 规范的等 到目前为止我注意到的一些示例包已发布两次 golang
  • 使用 mgo 驱动程序进行 mongo 聚合查询

    我在 mongodb 中有以下查询 db devices aggregate match userId v73TuQqZykbxFXsWo state true project userId 1 categorySlug 1 weight
  • 单值上下文中的多值错误

    我在编译 GO 代码时遇到此错误 multiple value fmt Println in single value context 我正在尝试创建一个函数 该函数接受可变数量的整数并将每个变量打印在一行上 GO package main
  • Golang 结构体初始化

    有一个像这样的简单结构 type Event struct Id int Name string 这两种初始化方法有什么区别呢 e1 Event Id 1 Name event 1 e2 Event Id 2 Name event 2 为什
  • 测试 gRPC 服务

    我想测试用 Go 编写的 gRPC 服务 我使用的示例是 Hello World 服务器示例grpc go 仓库 https github com grpc grpc go blob master examples helloworld g
  • “http:多个response.WriteHeader调用”有什么不好的影响?

    尽管我发现 http 多个响应 WriteHeader 调用 例外 但我的服务器表现良好 此异常不会导致我的服务器出现恐慌或行为异常 我进行了很多搜索 但只找到了如何解决这个问题 没有文档描述异常的不良影响 有人可以帮我找出为什么 http
  • 如何将长 Go 模板函数拆分为多行?

    我有一个很长的printf调用 Go 模板 例子 printf mongodb s s s s authSource admin replicaSet s readPreference nearest w majority Values r
  • 如何读取 UDP 连接直至超时?

    我需要读取 UDP 流量 直到超时 我可以通过在 UDPConn 上调用 SetDeadline 并循环直到出现 I O 超时错误来做到这一点 但这看起来很黑客 基于错误条件的流量控制 下面的代码片段看起来更正确 但并没有终止 在生产中 这
  • 从 Golang 调用 C 函数

    我想在 Golang 中编写控制器逻辑并处理 json 和数据库 同时在 C 中使用我的数学处理模型 在我看来 调用 C 函数的开销必须尽可能低 就像设置寄存器 rcx rdx rsi rdi 一样 执行一些操作fastcall 并获取 r

随机推荐