【问题标题】:Is shared standard C library first initialized by kernel?共享标准 C 库是否首先由内核初始化?
【发布时间】:2015-07-25 05:03:29
【问题描述】:

我试图了解链接器和加载器的操作,以及有关程序实际编译和执行方式的内存地址(物理或虚拟)。我遇到了两条信息,形成了我自己的理解版本。

第一条信息:

W.5.1 共享对象 在一个典型的系统中,许多程序将 正在运行。每个程序都依赖于许多功能,其中一些 这将是标准 C 库函数,如 printf()、malloc()、 strcpy() 等,有些是非标准或用户定义的函数。如果 每个程序都使用标准 C 库,这意味着每个程序 通常会存在此特定库的唯一副本 在其中。不幸的是,这会导致资源浪费,降低 效率和性能。 **由于C库是通用的,所以 最好让每个程序都引用公共的,一个实例 库,而不是让每个程序都包含该库的副本。 这是在链接过程中实现的,其中一些 对象在链接期间被链接,而一些在链接期间完成 运行时(延迟/动态链接)。 **

第二条信息:

C 库

主要文章:请参阅 C 库,创建 C 库 前面的一件事: 当您开始使用内核时,您没有 C 库 可用的。你必须自己提供一切,除了少数 编译器本身提供的片段。您还必须移植一个 现有的 C 库或自己编写一个。 C 库实现了 标准 C 函数(即,在 等)并以适合的二进制形式提供它们 用于与用户空间应用程序链接。除了标准 C 函数(在 ISO 标准中定义),C 库可能(和 通常确实)实现进一步的功能,这可能或可能 不是由某些标准定义的。标准 C 库什么也没说 例如,关于网络。对于类 Unix 系统,POSIX 标准定义了对 C 库的期望;其他系统 可能根本不同。需要注意的是,为了 实现其功能,C 库必须调用内核函数。 因此,对于您自己的操作系统,您当然可以使用现成的 C 库并 只需为您的操作系统重新编译它 - 但这需要您告诉 库如何调用你的内核函数,以及你的内核实际上 提供这些功能。更详细的示例可在 库调用,或者,您可以使用现有的 C 库或创建自己的 C 库。

我理解的方式:

当计算机启动时,它首先无法访问 C 库,而是必须使用机器代码。但是在引导代码的帮助下,它最终会开始加载操作系统。在此示例中,我将假设一台计算机加载 linux 操作系统。自然会加载一个linux内核。

当启动 linux 内核时,这也意味着标准 C 库(例如 printf 等基本函数)也被加载到低内存(分配给内核空间的 RAM 部分)。假设用户使用标准 C 库中的 printf() 编写了一个简单的代码。用户将编译此代码,在此过程中,链接器将为 printf() 进行“引用”,暗示 printf() 函数驻留在低内存中的位置。 执行此代码时,加载程序会将保存在 HDD 中的可执行文件加载到高内存(分配给用户空间的 RAM 部分)。当进程遇到 printf() 函数时,它会跳转到包含 printf() 函数开始的低内存地址。

我说的对吗?如果不是,我哪里错了?

【问题讨论】:

标签: linux linux-kernel linker loader


【解决方案1】:

你错了。

1.) 无需将 libc 放入内核。它不会影响任何低级系统或硬件相关组件。

2.) libc.so 是普通的动态库。

现在有更多细节:

当您启动应用程序时,f.e.从 bash 控制台,bash 分叉并执行新进程。这是什么意思。实际上,这意味着操作系统创建地址空间环境并从 ELF 文件加载 .text .data .bss ,为堆栈保留虚拟空间。您可以在此处查看此映射:

sudo cat /proc/1118/maps 
00400000-00407000 r-xp 00000000 08:01 1845158                            /sbin/getty
00606000-00607000 r--p 00006000 08:01 1845158                            /sbin/getty
00607000-00608000 rw-p 00007000 08:01 1845158                            /sbin/getty
00608000-0060a000 rw-p 00000000 00:00 0 
00ff3000-01014000 rw-p 00000000 00:00 0                                  [heap]
...
7f728efd3000-7f728efd5000 rw-p 001bf000 08:01 466797                     /lib/x86_64-linux-gnu/libc-2.19.so
7f728efd5000-7f728efda000 rw-p 00000000 00:00 0 
7f728efda000-7f728effd000 r-xp 00000000 08:01 466799                     /lib/x86_64-linux-gnu/ld-2.19.so

7f728f1fe000-7f728f1ff000 rw-p 00000000 00:00 0 
7fffa122b000-7fffa124c000 rw-p 00000000 00:00 0                          [stack]
7fffa1293000-7fffa1295000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

但还有更多。加载这些段后,Linux 内核还将 ld-linux.so 加载到内存中(您可以在映射中看到它)。这个东西叫做动态链接器,实际上 ld-linux 负责所有动态库的加载。您可能知道,在编译应用程序的那一刻,您已经知道将使用的共享库列表。可以通过ldd命令查看

ldd /sbin/getty 
linux-vdso.so.1 =>  (0x00007fff4cfa6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2af2832000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2af2c24000)

这些东西必须放在 ELF 的某个地方(不知道具体在哪里)。所以加载后,ld-linux 使用这个列表并在预定义(标准)路径(如 /usr/lib 等)中找到所有需要的库。现在 ld-linux 可以只使用mmap 区域来定位动态库。这就是将 libc 加载到进程地址空间的方式。

【讨论】:

  • 谢谢。我想我需要学习更多才能完全理解这一点。但我还有一件事想问。我知道共享对象(.so 文件)可以通过动态链接在许多进程之间共享。那么这是否意味着当每个进程为共享库映射新的内存区域时,它会导致每个进程最终使用多个 RAM 部分来保存相同的代码副本(这将是共享库函数)。我说的对吗?
  • @do_os Linux 和其他 UNIX 变体对其进行了优化。虽然多个进程会将库的代码区域映射到其虚拟内存地址空间,但内核仅在物理内存中保留库文本段的一份副本(即使它在不同进程中映射到不同的虚拟地址)。当然,数据段是不能共享的,所以每个进程都有自己的。
  • @do_os 这不仅发生在库文本段中,而且几乎发生在任何被映射为只读的东西上:只要每个人都以只读方式映射某些东西,每个人都可以共享相同的底层副本。
  • @FilipeGonçalves 啊...然后我想我在原始问题中假设的部分似乎是正确的:内核在 RAM 中准备好库的原始副本,可以与其他进程共享(用于文本段)。感谢您的洞察力
  • @do_os 实际上物理内存的每一部分最后都由内核页面分配器管理。因此,如果我们以这种方式提出问题 - 一些 libc 部分将位于内核页面缓存中。何时访问 libc 功能的新应用程序将首先查看此页面缓存,并在此处看到该 libc。 duartes.org/gustavo/blog/post/…
【解决方案2】:

啊...那么我想我在最初的问题中假设的似乎是 部分正确:内核在 RAM 中准备好库的原始副本 可以与其他进程共享(用于文本段)。感谢您 为了您的见解

你比你想象的更正确 :) 看看这个:linux-vdso.so.1 => (0x00007fff4cfa6000) 这几乎是一个“标准 C 库(例如 printf 等基本函数)......也加载到低内存中”。好吧,不是在低内存:) 和非标准(就 C 而言)和大多数时间由 C 库而不是直接使用代码,但是是的:由内核作为 linux 上下文中的一组标准函数加载到用户空间中。 http://man7.org/linux/man-pages/man7/vdso.7.html

【讨论】:

    猜你喜欢
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 2019-02-04
    • 2016-02-01
    • 1970-01-01
    相关资源
    最近更新 更多