共享标准C库是否首先由内核初始化?

do_*_*_os 4 linux linker loader linux-kernel

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

第一信息:

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库并为您的操作系统重新编译它 - 但这需要您告诉库如何调用您的内核函数,并让您的内核实际提供这些函数.库调用中提供了更详细的示例,或者您可以使用现有的C库或创建自己的C库.您当然可以使用现成的C库并为您的操作系统重新编译它 - 但这需要您告诉库如何调用您的内核函数,并让您的内核实际提供这些函数.库调用中提供了更详细的示例,或者您可以使用现有的C库或创建自己的C库.

我理解的方式:

当计算机启动时,它首先没有对C库的任何访问权限,而是必须使用机器代码.但是在启动代码的帮助下,它最终将开始加载操作系统.在这个例子中,我将假设一台计算机加载Linux操作系统.当然会加载一个linux内核.

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

我对么?如果没有,我错在哪里?

Ale*_*pus 7

你错了.

1.)没有必要将libc放入内核.它不会影响任何低级系统或硬件相关组件.

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

现在更多细节:

当您启动应用程序时,可以从bash控制台,bash forks和execs新进程中获取.这是什么意思.实际上,这意味着操作系统创建地址空间环境并从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]
Run Code Online (Sandbox Code Playgroud)

但还有更多.加载thoose段后,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)
Run Code Online (Sandbox Code Playgroud)

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