对CPU和操作系统的一点见解

做到了MIT6.828的lab4后对操作系统感觉有了更深的理解,下面记录一下最近想到的问题。

1. CPU是怎么运行程序的,为什么会有代码段和数据段,为什么不合在一起?

现在才觉得自己真正看懂了冯诺依曼计算机体系结构图,几十年过去了这套体系依旧适用。


冯诺依曼体系结构

CPU和代码之间的交互的桥梁是内存单元,CPU内部的计算单元与内存代码交互的桥梁是通过寄存器。例如要运行一段代码:

int main() {
    int a = 1, b = 1;
    a = b + 1;
    return 0;
}

内核会创建一个进程,并解析该可执行文件的ELF头文件,找出其代码开始执行的入口地址,即int main,将int main的地址保存下来,存放在进程描述符的eip中。(当然还有很多信息需要记录,这里只是举个例子)。
当该进程被CPU调度时,就会将CPU中的eip寄存器赋值为int main的地址。这样程序就会从int main开始执行。当遇到a = b + 1时,对应的指令可能是

mov $0x0012, %eax
add $0x0013, %eax
mov %eax, $0x0012

其中0x0012, 0x0013是a, b变量值所存储的地址,指令a = b+1这个操作需要涉及到CPU的计算单元,而CPU与计算单元交互的都是寄存器,因此需要将数据移送到寄存器(eax: 累加寄存器)中进行操作。计算完再将寄存器的值保存到一个内存地址中(可以好好体会下冯诺依曼体系结构那张图)。
这段地址会被放在进程的数据段中,上述的三条指令是在代码段中(这就是为什么程序需要分段),如果数据和代码混合在一起的话,eip移动的距离就不会是固定的,比如一条指令地址是0x0000000,然后中间穿插几个数据的值,再接着0x00000010第二条指令。这样的话就需要在上一条指令中再记录第二条代码指令的地址,特别的繁琐,效率也低。

2. 操作系统也需要运行,那操作系统也算一个进程吗?

进程是操作系统中的一个概念,从问题1中可以看出,CPU能运行代码,只需要处理好寄存器和内存就可以(才疏学浅,欢迎指正!)。操作系统(内核)也是由代码组成,这些代码控制着硬件,当操作系统开始运行时,它也是运行在CPU上面的,但这时还没有进程的概念,能运行在CPU上全靠加载操作系统内核时,对内核代码ELF头文件的解析,所以只要有ELF头文件,有ELF头文件的解析方法,能设置寄存器的初始信息,那就可以让程序运行!

3. 那为什么要有进程呢?

现在操作系统正在运行中,CPU上面运行的是操作系统的程序,操作系统控制着CPU中的各种寄存器。
如果现在自己写了一个程序,想要在操作系统上运行它,那其实只要在操作系统中实现一个解析程序代码的模块就可以,最后抽象成一个命令。执行完这个程序后再跳回操作系统的代码。

操作系统开始运行该程序时,在内存中先记录下当前一些寄存器的值,例如eip的值
...
int main() {
    int a = 1, b = 1;
    a = b + 1;
    return 0;
}
...
运行到这里后再从内存中将eip等寄存器的值恢复。

但当操作系统运行到一个需要执行I/O指令的代码时,其就会陷入到I/O设备中,例如OUT指令,执行一个需要1s(夸张手法),相当于卡了1s,这1s内CPU是没事可做的(CPU只做计算指令)。
为了让CPU的效率增加,人为的增加了进程这个概念。进程就是用来保存程序当前执行的信息(内存,寄存器,状态等),因为有了虚拟地址,每个进程的内存看起来是相互隔离互不影响的。当CPU空闲时,就会调度新的进程来运行(因为进程有寄存器,状态等信息,CPU读取后就可以运行它)。
个人理解,程序+程序运行信息 = 进程,为了就是让程序运行时可以让出CPU

4. 扩展到多核CPU,本质上就是多个eip在内存中移动。

可能两个eip会同时移动到同一个指令,这个指令是修改内存的指令,哪个先哪个后很重要,因此需要对其进行加锁,这就是加锁的由来,如果单核CPU的话,即使多个进程,你也不需要加锁。

你可能感兴趣的:(对CPU和操作系统的一点见解)