操作系统的三大法宝
存储程序计算机、函数调用堆栈机制、中断。
三者中的存储程序计算机,就是冯诺依曼结构没什么好说的,接触计算机的都知晓,然后是函数调用堆栈是比较基础性的 概念,对理解操作系统的一些关键代码是非常重要,其次是中断。
首先来看堆栈,堆栈是C语言程序运行时必须使用的记录函数调用路径和参数存储的空间,堆栈的具体作用有:记录函数调用框架、传递函数参数、保存返回值的地址、提供函数内部局部变量的存储空间等。
下面具体看一下堆栈相关的内容;
一、堆栈相关的寄存器
ESP:堆栈指针(stack pointer)
EBP:基址指针(base pointer)在C语言中用途记录当前函数调用基址。如果当前函数调用比较深,每一个函数的EBP是不一样,函数调用堆栈就是由多个逻辑上的堆栈叠起来的框架,利用这样的堆栈框架实现函数的调用和返回。
对X86体系结构来说,堆栈空间是从高地址向低地址增长的。
二、堆栈操作
push:栈顶地址减少4个字节(32位),并将操作数放入栈顶存储空间
pop:栈顶地址增加4个字节(32位),并将栈顶存储单元的内容放入操作数
三、其它关键寄存器
CS:EIP总是指向下一条的指令地址,这里用到了CS寄存器,也是就是代码段寄存器和EIP总是指向下一条指令地址。如果程序比较简单的一个小程序,它只有一个代码段,所有的EIP前面的CS代码段寄存器的值都是相同的。当然这种是一个特例,一般程序都至少会使用到标准库,整个程序会有多个代码段
顺序执行:总是指向地址连续的下一条指令。
跳转/分支:执行这样的指令,CS:EIP的值会根据程序的需要被修改。
call:将当前CS:EIP的值压入堆栈栈顶,CS:EIP指向被调用函数的入口地址。
ret:从栈顶弹出原来保存在这里的CS:EIP的值,放入CS:EIP中。
堆栈是C语言程序运行时必需的一个记录函数调用路径和参数存储的空间。堆栈实际上已经在CPU内部给我们集成好了的功能,是CPU指令集的一部分。比如32们的X86指令集中就有pushl和popl指令用来做压栈和出栈操作,enter和leave指令更进一步对函数调用堆栈框架的建立和拆除进行封装,帮我们提供了简洁的指令来做函数调用堆栈框架的操作。堆栈里特别关键的就是函数调用堆栈框架。
四、用堆栈来传递函数的参数
对32位X86CPU来讲,通过堆栈来传递参数的方法是从右到左依次压栈。
五、函数是如何传递返回值的
这里涉及保存返回值和返回地址的方式,保存返回值,就是程序用EAX寄存器来保存返回值。如果有多个返回值,EAX寄存器返回的是一个内存地址,这个内存地址里面可以指向很多的返回数据,EAX 寄存器可以保存返回地址。
函数还可以通过参数来传递返回值,如果参数是一个指针且该指针指向的内存空间可以写,那么函数体的代码可以把需要返回的数据写入该内存空间。这样调用函数的代码在函数执行结束后,就可以通过该指针参数来访问这些函数返回的数据。
六、堆栈还提供局部变量的空间
函数内部的局部变量是通过堆栈来存储的,目前的编译器一般在函数开始执行时预留出足够的栈空间来保存函数体内部的局部变量。
七、编译器使用堆栈的规则
C语言编译器对堆栈有一套使用规则,而且不同版本编译器规则还有不同。
二:中断部分
操作系统有了中断才有了多道程序,在没有中断的机制前,计算机只能一个程序一个程序地执行,也就是批处理,而无法多个程序并发工作。有了中断机制的CPU帮我们做了一件事情,就是当一个中断信号发生时,CPU把当前正在执行的程序的CS:EIP寄存器和ESP寄存器等都压到了一个叫内核堆栈的地方,然后把CS:EIP指向一个中断处理程序的入口,做保存现场的工作,之后执行其他程序,等重新回来时再恢复现场,恢复CS:EIP寄存器和ESP寄存器等,继续执行程序。