当前位置: 首页 > 面试题库 >

为什么CGO_ENABLE对虚拟内存有这种影响?

邵诚
2023-03-14
问题内容

我有一个用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”;该代码可从主站点下载)默认情况下会生成静态链接的二进制文件。这意味着,这些二进制文件仅依赖于OS内核提供的所谓“系统调用”,而不依赖于OS(或第三方)提供的任何共享库。

在基于Linux的平台上,情况并非完全如此:在默认设置下(在Linux for Linux上构建,即不进行交叉编译),生成的二进制文件实际上与libc
或与链接libpthread(间接通过libc)。

这种“扭曲”来自Go标准库必须与OS交互的两种需求:

  1. net包需要DNS解析。
  2. os程序包需要的用户和组查找。

这里的问题有两个:

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

  • 从此以后,任何典型的类似UNIX的系统都会使用称为“名称服务开关”¹的特殊功能“ NSS”来提供这两项任务。

NSS提供了可插入模块,这些模块可以用作提供特定类型查询的数据库:DNS,用户/组数据库等(例如“服务”的知名名称等)。用户/组数据库的非标准提供程序的一个相当普遍的示例是与LDAP服务器联系的本地服务。

在典型的基于GNU / Linux的OS上,NSS是由实现的 libc(在不太典型的系统上,它可以由单独的共享库提供,但这变化不大)。

自-再次,通常,
-该libc是它的API方面具有相当稳定的库(它甚至还提供了版本的符号是面向未来的),转到作者理所当然地决定,链接到的libc导入符号的最小的子集(主要是getaddrinfogetnameinfogetpwnam_r等)默认情况下可以执行,因为它对于99%的案例都是安全的,否则,那些必须处理这些案例的人通常都知道该怎么做。

因此,默认情况下cgo处于启用状态, 并用于 使用NSS实施这些查找。

如果cgo被禁用,则Go编译器将链接到其自己的后备实现中,以尝试模仿完整的NSS实现的作用的子集(即,解析/etc/resolv.conf并使用其中的信息来直接查询此处列出的DNS服务器解析/etc/passwd/etc/group转换为服务用户/组数据库查询)。

如您所见,在最严重的情况下,

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

相反,在cgo禁用的情况下,以上两种情况均不会发生。您有更多的stdlib代码以静态方式链接,但是看起来在默认情况下,就整体累积RSS使用而言,默认情况仅胜于后一种情况。

考虑研究此查询的输出以
获得更多乐趣;-)

¹不要与Mozilla的混淆libnss



 类似资料:
  • 虚拟内存是一种方便的方法,可以在进程之间隔离内存,并为每个进程提供自己的地址空间。它通过将虚拟地址转换为物理地址来工作。 我已经非常熟悉虚拟内存的工作原理和实现。我不知道的是虚拟内存相对于直接映射内存的性能影响,直接映射内存不需要翻译开销。 请不要说没有开销。这显然是错误的,因为遍历页表需要多次内存访问。TLB未命中可能很少,因此对性能的影响可以忽略不计,但是,如果是这种情况,应该有证据证明这一点

  • 主要内容:虚拟内存如何工作?,按需分页,虚拟内存管理系统的快照虚拟内存是一种存储方案,为用户提供了一个拥有非常大的主内存的幻觉。 这是通过将辅助存储器的一部分作为主存储器来完成的。 在这种方案中,用户可以加载比可用主存更大的进程,因为存在内存可用于加载进程的错觉。 操作系统不是在主内存中加载一个大进程,而是在主内存中加载多个进程的不同部分。 通过这样做,多程序的程度将会增加,因此CPU利用率也会增加。 虚拟内存如何工作? 在现代语言中,虚拟内存近来变得非常普

  • 本文向大家介绍请问虚拟内存和物理内存怎么对应相关面试题,主要包含被问及请问虚拟内存和物理内存怎么对应时的应答技巧和注意事项,需要的朋友参考一下 参考回答: 1、概念: 物理地址(physical address) 用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。   虽然可以直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从0字节一直到最大空量逐字节的编号的大数组,然后把

  • 处理器的虚拟内存子系统为每个进程实现了虚拟地址空间。这让每个进程认为它在系统中是独立的。虚拟内存的优点列表别的地方描述的非常详细,所以这里就不重复了。本节集中在虚拟内存的实际的实现细节,和相关的成本。 虚拟地址空间是由CPU的内存管理单元(MMU)实现的。OS必须填充页表数据结构,但大多数CPU自己做了剩下的工作。这事实上是一个相当复杂的机制;最好的理解它的方法是引入数据结构来描述虚拟地址空间。

  • 问题内容: 我希望有人能够提供一些有关Java虚拟机的根本差异的见解,从而使Java虚拟机可以很好地实现线程而无需使用全局解释器锁(GIL),而Python则需要这样做。 问题答案: Python(该语言)不需要GIL(这就是为什么它可以在JVM [Jython]和.NET [IronPython]上完美实现的原因,并且这些实现可以自由地使用多线程)。CPython(流行的实现)一直使用GIL来简

  • 问题内容: 导入标准“日志记录”模块会对一堆虚拟条目造成sys.modules污染: 因此,导入此软件包会将额外的名称放入sys.modules中,除了它们不是模块以外,仅引用None。其他模块(例如xml.dom和编码)也有此问题。为什么? 编辑: 在bobince的答案的基础上,有一些页面描述了功能的起源(请参阅“ sys.modules中的虚拟条目”部分)和功能的未来。 问题答案: 中的值是