注:本文档为《从0学x86操作系统》课程配套的学习文档,提供相应的辅助学习资料和答疑勘误。 有关该课程的信息,请点击这里访问:https://study.163.com/provider/1017884735/index.htm 在阅读本文档时,如有疑问和建议,欢迎在下方留言或者直接联系我。

本课时介绍如何将loader中获取的启动信息(如内存容量)传递给内核。具体来说,我们会了解x86上C语言中如何使用栈向函数传递参数。本课时已经假定你对栈有基本的了解,所以不会在其中特别细致地讲解有关栈的概念和基本使用。
注:本课时难点在于理解函数调用时出入栈的过程,需要花较多时间和精力理解。

x86的栈

保护模式下,x86的栈单元大小为32位,压栈时总是先esp-4,再写入数据;出栈过程则正好相反。
image.png
在C函数中,编译器会根据定义的局部变量、计算过程、函数调用按照一定的规范自动规划栈的使用。具体的使用方法如下:

  1. 保存局部变量和数据
  2. 传递参数:从参数列表右侧往左压入栈
  3. 保存返回地址
  4. 通过ebp+偏移取调用者的传入的参数和自己的局部变量

image.png

课程中栈的使用

课程中实际上是做了从loader到kernel的两级函数调用。

load_kernel() —> ((void ()(boot_info_t ))SYS_KERNEL_LOAD_ADDR)(&boot_info) -> kernel_init(boot_info)

我们所做的工作实际上理解编译器对栈的分配处理规则,取出load_kernel传递过来的参数,再通过栈传递给kernel_init。视频中给出了两种处理方法,实际上还有一种更简单的只需要一条指令的第三种方法。

  1. # 第一种方法
  2. # push %ebp
  3. # mov %esp, %ebp
  4. # mov 0x8(%ebp), %eax
  5. # push %eax
  6. # 第二种方法
  7. # mov 4(%esp), %eax
  8. # push %eax
  9. # 第三种方法,直接将内存中的值压栈
  10. push 4(%esp)
  11. # kernel_init(boot_info)
  12. call kernel_init

参考资料

  • 有关函数调用及栈的知识:Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 1: Basic Architecture(第6章 149页)