Linux kernel header.S源码,为什么清零BSS时需要_end+3?

标签 linux assembly linux-kernel x86 bootloader

在 Linux 源代码树中文件 arch/x86/boot/header.S具有与此类似的 x86 代码以在调用 main 之前清除 BSS 部分:

...
# Zero the bss
    movw    $__bss_start, %di
    movw    $_end+3, %cx
    xorl    %eax, %eax
    subw    %di, %cx
    shrw    $2, %cx
    rep; stosl
...

为什么 _end 地址添加了 3?为什么不是 movw $_end, %cx 而不是 movw $_end+3, %cx

最佳答案

如果代码逐字节清除 BSS 部分 movw $_end, %cx 就足够了。但是,此代码不会将 BSSSTOSB 归零,它们使用 STOSL。一次存储 32 位通常比 8 位更有效。

STOSL将存储 EAX(用 xorl %eax, %eax 设置为零)足够的时间来将 BSS 的整个范围清除为 0。 +3 确保如果 BSS 部分的长度 ($_end-$__bss_start) 不能被 4 整除,则计算需要清除的 DWORD 的数量将被四舍五入。如果没有发生这种舍入,那么在大小不能被 4 整除的情况下,最后的字节可能不会被清除。

此处进行的计算假定 __bss_start 是指向 BSS 段开头的指针,_end 是指向 BSS。计算要清除的 32 位 DWORD 数的公式实际上是:

NUMDWORDS=(_end+3-__bss_start) >> 2

shr​​w $2, %cx(计算中的>>2)是整数除以 4,结果总是向下舍入。我们将 +3 添加到字节数,以便在除以 4 后有效地向上舍入到最接近的 DWORD 数。然后,此值用作 DWORD STOSL 的数量将设置为零。

关于Linux kernel header.S源码,为什么清零BSS时需要_end+3?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13065207/

相关文章:

linux - Docker 容器 : lsmod not found

linux - 如何根据硬件规范确定各种操作的基线?

c - 内存中的程序及其内存映射

assembly - 指令集是如何标准化的?

c - 与GCC中的间接访问相比,为什么直接访问结构成员会产生更多的汇编代码?

c++ - SDL_SetVideoMode问题

linux - 无法在 Linux 上启动 postgres

linux - 启动服务应用程序并将其通过管道传输到当前终端

linux - Linux 内核在进程暂停时长方面提供什么保证?

arrays - 将 32 位寄存器 [esi] 的内存地址移至汇编语言 x86 中的 8 位低位寄存器