从本质上来说操作系统就是把底层硬件抽象成了一层虚拟机,所以说计算机本身就是一个虚拟机。计算机本身并不会做任何事情,它就是一堆铁疙瘩,即使给它加电它也不会做任何事情,cpu只有在程序的指挥下才会做事情。所以,操作系统的启动就是一个自举的过程,上电的一刹那间主板上的一块ROM芯片中的代码会被自动映射到内存的低地址空间,这块ROM芯片中存的就是BIOS,后续的过程可以参见我的另一篇博客http://jiuyugu.blogspot.com/2016/03/blog-post.html,本文不做详细讨论。
在冯诺依曼体系中,计算机有五大部件,分别是运算器、控制器、存储器、输入设备和输出设备。其中最核心的是运算器、控制器和存储器。运算器在控制器的控制下从不断从存储器中读取数据处理,在计算机内部的存储器速度是最快的,称为寄存器,之所以称为寄存器是因为只里面的数据刷新频率很快,然后是一级缓存、二级缓存、三级缓存,再到外面就是内存了,从内到外造价越来越低,存取速度越来越慢,容量越来越大。这里牵扯到一个问题,为什么缓存可以提高速度? 程序的局部性原理,就是我们常说的二八法则,一段程序中执行频率最高的代码往往只有20%,80%的代码很少用到,而这20%的代码完成了整个程序80%的功能。我们可以将这20%的代码缓存到cpu一级缓存或者二级缓存中,因为cpu中的cache的速度和cpu的时钟频率最接近,这样就可以提高程序运行的速度,关于这部分的详细介绍可以参见《深入理解计算机系统》一书。在我的debian系统上可以用lscpu指令查看有关cpu和cache的简略信息:可以看到从L1 cache到L3 cache,容量越来越小了
1 [email protected]:~$ lscpu 2 Architecture: x86_64 3 CPU op-mode(s): 32-bit, 64-bit 4 Byte Order: Little Endian 5 CPU(s): 4 6 On-line CPU(s) list: 0-3 7 Thread(s) per core: 2 8 Core(s) per socket: 2 9 Socket(s): 1 10 NUMA node(s): 1 11 Vendor ID: GenuineIntel 12 CPU family: 6 13 Model: 42 14 Model name: Intel(R) Core(TM) i5-2430M CPU @ 2.40GHz 15 Stepping: 7 16 CPU MHz: 825.750 17 CPU max MHz: 3000.0000 18 CPU min MHz: 800.0000 19 BogoMIPS: 4789.14 20 Virtualization: VT-x 21 L1d cache: 32K 22 L1i cache: 32K 23 L2 cache: 256K 24 L3 cache: 3072K 25 NUMA node0 CPU(s): 0-3
一般缓存算法设计的思路都是:最近最少使用原则,将最近最少使用的数据从缓存中移除,毕竟缓存的空间是有限的,要是缓存和内存的价格一样,那就没必要设计缓存了。程序的局部性原理分为空间局部性和时间局部性:空间局部性是指一段代码被访问了,那么它周围的代码也极有可能被访问到。时间局部性是指某一时刻一段代码被访问了,那么过一会儿这段代码极有可能被再次访问。
为了衔接计算机系统中各个速度比cpu慢的设备,早期的主板上集成有北桥芯片和南桥芯片(现在的主板可能已经不是这么设计了),南桥芯片是将各慢速设备汇总起来一起接入北桥芯片,所以桥接芯片说白了就是汇总各外部设备最终完成和cpu的交互。south bridge上接的通常称为ISA总线,早起的PCI总线都是接到南桥上的,接入北桥的称为PCI--E,PCI--E总线的速度比PCI总线的速度要快得多得多。常见的磁盘总线都是PCI格式的,SCSI、IDE、SATA统称为PCI总线,PCI只是一种统称。那么问题来了,计算机接了这么多的外部设备,cpu如何区分不同的IO设备呢? 类比计算机区分和互联网通信的各个进程的方法,计算机区分不同的和外部通信的进程靠的是套接字,也就是ip地址+端口号。这里cpu区分不同IO设备靠的也是端口号,称为IO端口,在一台计算机上IO端口的数目也是65535个。任何一个IO设备通过IO总线接入计算机的时,它必须一开机就申请一批连续的IO端口。
之所以把操作系统称为虚拟机,是因为我们只有一块cpu芯片(可能是多核心的),只有一块内存,鼠标只有一个,键盘只有一个........但是每个进程都好像独占这一整套资源。cpu通过时间片轮转的方式讲一个cpu芯片虚拟成多个cpu,内存的虚拟通过分页机制,将内存切割成一个个固定大小的页面(这部分参见我的另一篇博客http://jiuyugu.blogspot.com/2016/03/linux.html 以及《程序员的自我修养--链接、装载与库》)。好了,现在已经把计算机系统中最重要的两个部件运算器和存储器虚拟出来了(其实就是虚拟的cpu和内存),剩下的那些IO设备如何虚拟呢?其实在IO虚拟不需要专门去做,因为当前哪个进程获得了系统使用权,IO设备就交给整个进程。
cpu芯片只有一块,在某一时刻,要么是内核进程在上面运行,要么是用户空间进程在上面运行,内核在cpu上运行时称为内核模式,进程在cpu上运行时称为用户模式。而在内存中内核占据的那段内存空间称为内核空间,用户进程占据的空间叫用户空间。用户模式时,进程是不能直接控制硬件的。这是因为在cpu内部,cpu制造商将cpu能运行的指令划分为4层(仅对x86架构而言),ring0,ring1,ring2,ring3,由于历史原因,ring1和ring2并没有使用,linux只用了ring0和ring3。ring0称为内核模式,也称为特权指令模式,可以直接操控硬件,ring3是用户模式,可以执行一般指令。
当一个运行中的进程要打开文件或者操作麦克风,它发现自己没有权限执行特权指令,于是就会发起系统调用,一旦产生系统调用进程就会退出,从用户模式切换到特权模式,称为模式切换。由内核负责将数据转载至物理内存,再复制到用户空间。这里就牵扯到进程的状态了,这里简单介绍几个。就绪状态:就绪是指在所有进程队列中,这个进程所需的所有资源都已经准备好了。没有就绪的称为睡眠状态,而睡眠状态又分为可中断睡眠和不可中断睡眠,他俩的区别是:可中断睡眠是指随时可以唤醒的,不可中断睡眠是指内核为它准备的数据还没有准备好,即使唤醒它,它也不能干活。可中断睡眠不是因为资源没有准备好而睡眠,只是一个阶段的活儿干完了,下一阶段的活儿还没来,于是它就去睡觉了,你可以随时叫醒它,所以称可中断睡眠。而不可中断睡眠一般是因为IO而进入睡眠状态的。