X86 节bss并将其加载到内存中

X86 节bss并将其加载到内存中,x86,operating-system,elf,X86,Operating System,Elf,这里写着: main()中的第一步是调用bss_init(),这将清除 内核的“BSS”,这是对 应初始化为全零。在大多数C实现中, 当您在函数外部声明变量而不提供 作为初始值设定项,该变量进入BSS因为一切都结束了 零,则BSS不会存储在加载程序带入的映像中 记忆。我们只需使用memset()将其归零 我强调了一个我不明白的部分。为什么bss部分“等于0”会导致这种行为?当且仅当该问题不依赖于实现时,该问题才有意义。为BSS存储的不是全部为零的N个字节,而是一个长度 将BSS与数据部分分离的全

这里写着:

main()中的第一步是调用bss_init(),这将清除 内核的“BSS”,这是对 应初始化为全零。在大多数C实现中, 当您在函数外部声明变量而不提供 作为初始值设定项,该变量进入BSS因为一切都结束了 零,则BSS不会存储在加载程序带入的映像中 记忆。我们只需使用memset()将其归零


我强调了一个我不明白的部分。为什么bss部分“等于0”会导致这种行为?当且仅当该问题不依赖于实现时,该问题才有意义。

为BSS存储的不是全部为零的N个字节,而是一个长度

将BSS与数据部分分离的全部目的是为了节省空间。因此,它不会导致行为,而是使行为发生。

您可以将其视为可以用零初始化的所有内容的运行长度编码


那么,我们把什么归零呢

加载表示需要N字节BSS的映像时分配的内存


请注意,在非玩具操作系统中,所有BSS页面通常从映射到单个零物理页面(共享系统范围)的写时拷贝开始。当进程弄脏这样一个页面时,它会触发一个“小”页面错误,内核会给它一个私有的归零页面作为现在弄脏的虚拟页面的备份。(从轻微故障返回时,第一次出现故障的store指令成功执行,导致TLB未读取新更新的页表。)

其工作方式是链接器脚本(
kernel.lds.S
)具有
/*BSS(零初始化数据)位于其他所有内容之后。*/\u start\u BSS=。bss:{*(.bss)}_end_bss=
.bss
部分位于内核的末尾。由于所有变量和数据都是零,链接器不会将它们物理地放在文件中(为什么要在文件中添加一堆零空间)。它只是假设一旦在内存中,它们就会被调零。观察链接器脚本创建的一些符号
\u start\u bss
\u end\u bss
。这些符号将包含BSS开始和结束的虚拟内存地址。@Gilgamesz:作为我上一条评论的继续,下一个问题是它们实际上在哪里设置为零。这是在
main
内部
src/threads/init.c
中完成的。您将在该文件的开头看到对
bss_init()的调用它执行
外部字符\u start\u bss、\u end\u bss;memset(&&u start_bss,0,&&u end_bss-&&u start_bss)。基本上,它使用链接器创建的变量和BSS段的起始地址和结束地址,并使用
memset
@Gilgamesz将它们全部设置为零。最终结果是链接器脚本说特殊的
。BSS
部分将位于文件的末尾,我将声明它们的虚拟内存地址,它将取决于某人(在本例中,函数
main
将它们归零)。归零失败可能会导致未定义的行为,因为代码可能实际期望零初始化数据(这就是为什么编译器首先将它们放在
.bss
部分!),而不是内存中的垃圾。@Gilgamesz:假设您编写了一些C代码,并且您的程序包含一个500兆字节的全局变量数组。因为它是全局变量,所以500兆字节数组必须设置为零(这是标准要求的).但是等一下,如果你将500兆字节的零存储在文件中,那么你将得到一个500mb+的程序,该程序的结尾全部为零。那么,当链接器可以说“嘿,我有一个500mb的BSS节,它不在文件中,但出现在程序(内核)的结尾时,为什么要将这些零放在文件中呢这就是这一部分有多大,在使用前需要将其归零。@Gilgamesz:我对Pintos和其他一些受学术启发的内核略知一二。我非常了解x86上的操作系统设计,并利用这些常识在出现特定问题时找到我要找的东西。Basi凯莉,我不知道马上回答你的问题的具体细节,在评论之前我不得不回顾一下代码。近年来,Pintos一直是其他SO问题的主题。