为什么CGO_ENABLE对虚拟内存有这种影响?
我有一个用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/statusVmSize: 1084052 kB
VmRSS: 12524 kB
CGO_ENABLED = 0(守护进程已运行约30个小时)
$ grep -E 'VmSize|VmRSS' /proc/15160/statusVmSize: 110232 kB
VmRSS: 9756 kB
守护程序未使用CGO依赖的程序包或函数。其他Go编写的程序显示相同的行为。我知道VSZ和RSS之间的区别,而且有趣的是,这种行为的本质是什么?为什么用CGO_ENABLED
= 1编译的程序要求从内核提供这么多的内存?
我希望回答的形式不是“不用担心,VSZ只是一个虚拟内存,实际上它没有被进程使用”。
回答:
我可以做出有根据的猜测。
您可能知道,“参考” Go实现的编译器(历史上称为“
gc”;该代码可从主站点下载)默认情况下会生成静态链接的二进制文件。这意味着,这些二进制文件仅依赖于OS内核提供的所谓“系统调用”,而不依赖于OS(或第三方)提供的任何共享库。
在基于Linux的平台上,情况并非完全如此:在默认设置下(在Linux for Linux上构建,即不进行交叉编译),生成的二进制文件实际上与libc
或与链接libpthread
(间接通过libc
)。
这种“扭曲”来自Go标准库必须与OS交互的两种需求:
net
包需要DNS解析。os
程序包需要的用户和组查找。
这里的问题有两个:
Linux 本身 (即内核,而不是整个操作系统)不提供任何执行这些任务的方法。
从此以后,任何典型的类似UNIX的系统都会使用称为“名称服务开关”¹的特殊功能“ NSS”来提供这两项任务。
NSS提供了可插入模块,这些模块可以用作提供特定类型查询的数据库:DNS,用户/组数据库等(例如“服务”的知名名称等)。用户/组数据库的非标准提供程序的一个相当普遍的示例是与LDAP服务器联系的本地服务。
在典型的基于GNU / Linux的OS上,NSS是由实现的 libc
(在不太典型的系统上,它可以由单独的共享库提供,但这变化不大)。
自-再次,通常,
-该libc
是它的API方面具有相当稳定的库(它甚至还提供了版本的符号是面向未来的),转到作者理所当然地决定,链接到的libc
导入符号的最小的子集(主要是getaddrinfo
,getnameinfo
,getpwnam_r
等)默认情况下可以执行,因为它对于99%的案例都是安全的,否则,那些必须处理这些案例的人通常都知道该怎么做。
因此,默认情况下cgo
处于启用状态, 并用于 使用NSS实施这些查找。
如果cgo
被禁用,则Go编译器将链接到其自己的后备实现中,以尝试模仿完整的NSS实现的作用的子集(即,解析/etc/resolv.conf
并使用其中的信息来直接查询此处列出的DNS服务器;解析/etc/passwd
并/etc/group
转换为服务用户/组数据库查询)。
如您所见,在最严重的情况下,
- 在
libc
被映射的,并 - 它被 初始化 并使用一些内存来满足自己的需要,例如NSS调用返回的明显的数据缓存。
相反,在cgo
禁用的情况下,以上两种情况均不会发生。您有更多的stdlib代码以静态方式链接,但是看起来在默认情况下,就整体累积RSS使用而言,默认情况仅胜于后一种情况。
考虑研究此查询的输出以
获得更多乐趣;-)
¹不要与Mozilla的混淆libnss
。
以上是 为什么CGO_ENABLE对虚拟内存有这种影响? 的全部内容, 来源链接: utcz.com/qa/419409.html