linux操作系统的主要构架如图1所示,我们知道,操作系统是通过管理CPU进程、存储器、文件系统、设备驱动、以及网络接口等相关部分来工作的,我们这里主要是通过分析关于CPU的操作即进程的管理执行来分析linux操作系统是怎样工作的。
一 linux操作系统工作的基础
linux操作系统的正常工作可以说有三个非常重要的部分,就是我们的存储程序原理、堆栈以及中断的支持。
1.存储程序计算机
我们都知道,冯.诺依曼体系结构为计算机的发展提供了不可磨灭的作用,冯.诺依曼的主要贡献就是提出并实现了“存储程序”的概念。
按照冯·诺依曼存储程序的原理,计算机在执行程序时须先将要执行的相关程序和数据放入内存储器中,在执行程序时CPU根据当前程序指针寄存器的内容取出指令并执行指令,然后再取出下一条指令并执行,如此循环下去直到程序结束指令时才停止执行。那么我们就可以将计算机的工作过程简化成如下图2所示。
图2计算机基本工作原理
而我们的linux操作系统就是以存储程序计算机的工作原理为基础去管理整个计算机以及整个计算机的执行工作流程。
2.栈
进程是CPU执行的基本单位,每个进程都有自己独立的内存空间,进程的内存地址空间如附录一所示。当然我们这里要说的就是进程的用户态的占空间以及8KB的内核栈,由于虚拟存储技术,每个进程都感觉自己享有完整的内存空间,在这里不加赘述。
栈为我们的函数调用以及进程的切换等等提供了不可磨灭的作用。当我们进行函数调用的时,用户态的栈会保存我们的返回地址、相关寄存器、函数参数以及局部变量等等一些相关条件;当我们进行系统调用或者其它中断的时候,内核栈会同样为我们保存返回地址、相关寄存器等一些相关信息,可以说栈是我们系统的正常执行必不可少的条件。
3.中断
中断是指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。中断技术的出现为多任务计算机提供了不可磨灭的作用,中断使得进程能够并发的去执行,大大提高了CPU的利用率。中断是多进程能够正常执行以及进程间的切换的必不可少的要素。
以上的三方面是linux操作系统正常工作管理必不可少的部分。我们知道,进程是CPU执行的单位(这里暂时不考虑线程),下面我们通过分析内核是如何管理进程来实现任务的正常进行。
二 内核工作的微观分析
操作系统对进程的管理主要就是进程的管理和调度,我们为每个进程维护一个进程描述和以及进程间的关系。我们的内核的工作主要有两部分组成,首先运行有一个内核线程,然后就是一些中断处理程序的集合,我们在中断处理程序中要就行进程的调度。下面主要分析内核是如何执行进程的切换以及调度来实现操作系统的正常执行。
如图3所示,当前进程X在运行,当前的esp寄存器指向进程X的用户栈,eip指向进程X的代码区,假设进程X的时间片用完,这时候发生中断,那么CPU要做两件工作,1是将当前的eip和esp压入到进程X的内核栈,2是将esp指向进程X的内核栈,并将eip指向中断处理入口,进入到内核态。在进行SAVA_ALL即保存相关寄存器等之后,开始执行中断处理程序,进行的切换与调度就是发生在中断处理程序中。我们会根据相应的调度策略,选择下一个要执行的进程Next,并切换到进程Next。这里内核是通过执行switch_to(switch_to的代码见附录一所示)来进行进程切换的,在switch_to中我们主要进行了进程的寄存器register和进程的内核栈stack的切换。当进程切换完以后,我们的esp就指向了进程Y的内核栈,我们可以看到,在进程Y的内核栈中保存着进程Y被挂起以前的esp以及eip,我们进程弹栈操作,那么我们现在的eip指向的就是我们进程Y的代码区,而esp就只想进程Y的用户栈,这样通过esp和eip的的切换我们就切换到了进程Y继续执行,完成了整个进程从X切换到进程Y。
图3 中断以及进程切换和调度
附录一 进程的地址空间
附录二 switch_to源代码
#define switch_to(prev,next,last) do{
unsigned long esi,edi;
asmvolatile("pushflnt" /* Saveflags */
"pushl %%ebpnt"
"movl %%esp,%0nt" /* save ESP */
"movl %5,%%espnt" /* restore ESP */
"movl $1f,%1nt" /*save EIP */
"pushl %6nt" /*restore EIP */
"jmp __switch_ton"
"1:t"
"popl %%ebpnt"
"popfl"
:"=m" (prev->thread.esp),"=m"(prev->thread.eip),
"=a" (last),"=S" (esi),"=D" (edi)
:"m" (next->thread.esp),"m"(next->thread.eip),
"2" (prev), "d" (next));
} while (0)
首先恭喜您,能够认真的阅读到这里,如果对部分理解不太明白,建议先将文章收藏起来,然后对不清楚的知识点进行查阅,然后在进行阅读,相应你会有更深的认知。如果您喜欢这篇文章,就点个赞或者【关注我】吧!!