标准 C 库是否默认加载到 Linux 的主内存中?

Sum*_*shi 21 linux gcc linux-kernel

由于大多数 Linux 内核是用 C 语言编写的,所以当内核加载到主内存中时,标准 C 库是否也随 Linux 内核一起加载?

如果这就是用 C 编写的程序比其他程序消耗更少内存的原因,因为标准 C 库已经加载,因此与在 Linux 机器上运行时用其他语言编写的程序相比也更快(更少的页面错误)?

Ste*_*itt 44

内核是用 C 编写的,但它不使用 C 库(正如dave_thompson_085指出的那样,它是“独立的”)。即使这样做了,与内核一起加载以供内核使用的 C 库也仅对内核可用(除非内核以某种方式明确地允许用户空间访问它),因此它无助于减少程序的内存要求。

也就是说,在大多数情况下,最早的程序在内核启动后运行(initramfs 中的程序,尽管它们将使用自己的 C 库副本;最终,init)使用 C 库,因此它最终被映射在早期,很可能库中被广泛使用的部分将始终保留在物理内存中。

内核包含许多 C 库函数或变体(例如,printk代替printf)的实现;但他们并不完全遵循标准。在某些情况下,会改为使用编译器中C 库函数的实现。

(请注意,绝大多数用 C 以外的语言编写的程序最终都使用 C 库。)

  • 事实上,我想说大多数其他语言在某些时候依赖于 C 标准库,通常作为它们自己的运行时库的依赖项。 (2认同)
  • @jpaugh 在 Unix 上,它要么使用 C 标准库,要么重新实现大量特定于系统的东西,而且包装越少,就越难与 C 交互。在非基于 C/C++ 的系统上,情况会有所不同,但 OpenVMS 似乎是那里唯一的幸存者,就其幸存而言。 (2认同)

小智 21

标准 C 库是否默认加载到 Linux 的主内存中?

不。

内核启动的第一个用户空间进程称为init,其目的是启动其他所有进程,并获取僵尸子进程(父进程在退出之前退出的进程,因此没有其他进程可以获取其退出状态)。

因为init几乎总是使用标准 C 库,所以标准 C 库一init启动就加载到内存中。但这只是副作用,内核并不关心。

内核与标准 C 库无关。(内核是用独立的 C编写的,标准 C 库不可用的环境。)

  • 过去 init 曾经是静态链接的,但是对于功能更丰富的变体(包括 systemd),假设它链接到 libc.so 是正确的。 (4认同)

use*_*438 7

内核上下文中的“C 标准库”更像是一种抽象形式,这意味着有一些可重用的实用程序子例程执行 C 语言指定的操作。就像不是由 C 语言定义而是由内核本身定义的可重用子例程一样,它们不以“共享对象”(动态库)形式存在。相反,它们与内核一起编译并组装成一个内核可执行文件。

你可以在这里找到一些库代码,例如 memcpy、strcmp/strncmp https://elixir.bootlin.com/linux/latest/source/arch/x86/lib

因此,内核附带了自己的 C 库实现,并且不与任何其他用户态代码共享它,也不使用用户提供的 C 库来启动。事实上,每个用户态程序都可以做同样的事情,例如静态链接(包含、打包)一个 C 库,并且不与任何其他人共享。

回答你的最后一个问题,没有用户态程序与内核共享 C 标准库。


Áng*_*gel 6

不,内核不使用标准 C 库。

请注意,标准 C 库(通常 glibc 是与 Linux 一起使用的库)正在将 C 调用“转换”为内核系统调用。许多事情完全在用户空间完成,但它是建立在内核之上的。因此,使用二进制形式的 C 库并不是那么容易,它既可以在内核中使用,也可以在用户空间中使用。此外,他们的图书馆的目标通常是完全不同的。

现在,第二部分。每个程序都会加载 C 库本身,即使系统中的几乎每个 Linux 程序都链接到相同的标准 C 库。

您可以调用grep libc /proc/self/smaps几次,看看每次 libc 是如何映射到不同地址的(作为 ASLR 的结果)。这与 Windows 形成对比,在 Windows 中,即使没有显式加载,一些库(如 kernel32.dll(KnownDLL))映射到每个进程的相同位置。

在 Linux 上,程序(好吧,ld)将需要像其他动态库一样执行加载 libc 的步骤。尽管 libc 确实是最优化的代码段之一,因此它的加载和一般运行都很快。通常,在非常低的级别(查看 Ulrich Drepper 文章)对于普通程序来说是不值得的。

如果这就是用 C 编写的程序比其他程序消耗更少内存的原因

所以不行。内存空间不会消耗更少的内存。libc 仍将显示进程中 libc 的内存使用情况。

因为标准 C 库已经加载,因此速度也更快(页面错误更少)

然而,确实代码已经加载到内存中,因此它不需要从磁盘中获取它(它是按页完成的,但是您的程序需要的代码部分之前可能由另一个不同的人请求)。

事实上,Linux 会积极地将文件缓存在内存中。如果您有足够的内存,一旦您从磁盘加载程序,它将被缓存在内存中,并且不会再次从(慢)磁盘加载该代码。因此,您可以通过提前从磁盘加载它们/之前加载它们来实现相同的效果。

与在 Linux 机器上运行时用其他语言编写的程序相比?

由于这种微小的差异,您不太可能更快地找到它们。更有可能的是,我会考虑以下原因:

  • 编写良好的 C 代码效率更高
  • 更底层,这也体现了C编程是怎样的
  • 编译器能够生成更高效的代码
  • libc 是高度优化的实际上,有些功能是在汇编程序中实现的。
  • 大多数其他语言实际上是建立在 libc 之上的,而不是自己调用内核

其他语言也可能没有那么慢。您需要指定一对这样的程序,以便对它们进行平等比较,然后确定是否真的是 C 版本是否更快,以及差异实际上在哪里。

  • C 库是高度优化的,但现在最积极优化的实现是在编译器本身中——它可以使用它的内置函数,结合调用点的知识,生成比 C 库更快和/或更紧凑的代码. (2认同)