为什么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会对虚拟内存产生如此大的影响? 的相关文章

  • Golang 从管道读取读取大量数据

    我正在尝试读取一个正在被焦油化 流式传输到标准输入的存档 但我正在以某种方式读取far管道中的数据多于 tar 发送的数据 我像这样运行我的命令 tar cf somefolder my go binary 源代码是这样的 package
  • 正则表达式不匹配

    我正在尝试以下代码 d byte x01 x00 x00 x00 x00 x00 x00 x00 x00 x00 x00 x80J x13 x80SQ x80L xe0 x80 x92 x80L x80H xe0 r regexp Must
  • 如何获取文件的 ctime、atime、mtime 并更改它们

    如何使用 Go 获取文件的 ctime mtime atime 并更改它们 在 Go 1 1 2 中 os Stat只能获取mtime os Chtimes 可以更改 mtime 和 atime 但不能更改 ctime Linux ctim
  • 在 Go 中读取请求负载?

    我正在使用文件上传器 需要请求负载中的详细信息来裁剪它 func Upload w http ResponseWriter r http Request reader err r MultipartReader if err nil htt
  • 如何使用 golang 和 mgo 库在 mongodb 中创建文本索引?

    我正在尝试对集合进行全文搜索 但为了做到这一点 我需要创建一个文本索引 http docs mongodb org manual tutorial create text index on multiple fields http docs
  • 数据库连接最佳实践

    我有一个使用 net http 的应用程序 我使用 http 注册了一些处理程序 这些处理程序需要从数据库中获取一些内容 然后才能继续编写响应并完成请求 我的问题是连接到该数据库的最佳实践是什么 我希望它能够以每分钟 1 个请求或每秒 10
  • runtime.LockOSThread 是否允许子 goroutine 在同一个操作系统线程中运行?

    我明白在 Go 中 runtime LockOSThread https golang org pkg runtime LockOSThread将一个 goroutine 绑定到一个操作系统线程 并且不允许其他 goroutine 在该线程
  • GoLang - 坚持使用 ISO-8859-1 字符集

    我正在开发一个项目 我们需要将信息保存在具有 ISO 8859 1 表的旧数据库中 因此 在向数据库写入内容之前 我需要将其从 UTF 8 转换为 ISO 8859 1 每次从数据库检索它时 我都需要将其转换回 UTF 8 我试图使用图书馆
  • 无法理解 5.6.1。注意事项:捕获迭代变量

    我正在学习 Go 但无法理解 var rmdirs func for dir range tempDirs os MkdirAll dir 0755 rmdirs append rmdirs func os RemoveAll dir NO
  • GO并发编程测试

    我试图确保我的并发程序不存在以下情况 僵局 livelock 饥饿 我找到了以下工具http blog golang org race detector http blog golang org race detector 我尝试编译并运行
  • 给定方法值,获取接收者对象

    Go 有没有办法从方法值获取接收者对象 例如有没有这样的MagicFunc这将使以下程序输出字符串my info来自底层 Foo 实例 package main import fmt type Foo struct A string fun
  • 是否支持动态变量?

    我想知道Go中是否可以动态创建变量 我在下面提供了一个伪代码来说明我的意思 我将新创建的变量存储在切片中 func method slice make type for i 0 i lt 10 i var variable i i slic
  • 我们如何在 Golang 中组合多个错误字符串?

    我是 golang 新手 我的应用程序需要在循环中返回多个错误 稍后需要组合并作为单个错误字符串返回 我无法使用字符串函数来组合错误消息 在返回之前可以使用什么方法将这些错误合并为一个错误 package main import fmt s
  • 为什么我的 SQL 占位符没有被替换(使用 Go pq)?

    根据文档 我正在这样做 var thingname string asdf var id int err database QueryRow SELECT id from things where thing thingname Scan
  • 无法通过键获取 Gorilla 会话值

    我无法通过这种方式从会话中获取价值 它是nil session initSession r valWithOutType session Values key 完整代码 package main import fmt github com
  • 投射回更专业的界面

    我正在用 Go 编写一个游戏 在 C 中 我将所有实体类存储在 BaseEntity 类的数组中 如果一个实体需要在世界中移动 那么它将是一个从 BaseEntity 派生的 PhysEntity 但添加了方法 我尝试模仿这是 go pac
  • 在golang中获取TTFB(第一个字节的时间)值

    我正在尝试获取 TTFB 值和 Connect 值 c exec Command curl w Connect time connect TTFB time starttransfer Total time time total o dev
  • Golang中如何删除字符串的最后一个字符?

    我想删除字符串的最后一个字符 但在此之前我想检查最后一个字符是否是 如何才能做到这一点 以下是删除尾随加号的几种方法 package main import fmt strings func TrimSuffix s suffix stri
  • 如何从非英语字符串解析go中的月份

    我想将以下字符串解析为 go 中的日期 This item will be released on March 9 2014 我跟着this https stackoverflow com questions 14106541 go par
  • 错误“binary.Write:无效类型”是什么意思?

    下面显示的代码 我创建了一个结构类型并希望将其编码为二进制 但它显示binary Write invalid type main Stu错误 我读过一些类似的代码 但我找不到为什么我的代码不起作用 type Stu struct Name

随机推荐