Linux操作系统是如何工作的?破解操作系统的奥秘

一、操作系统工作的基础

 存储程序计算机最早是由著名数学家冯·诺依曼等人在1946年总结并明确提出来的,因此又被称为冯·诺依曼计算机。它的基本组成包括输入设备、输出设备、存储器、运算器和控制器。其基本的工作流程是:

第一步:将程序和数据通过输入设备送入存储器;

第二步:启动运行后,计算机从存储器中取出程序指令送到控制器去识别,分析该指令要求做什么事;

第三步:控制器根据指令的含义发出相应的命令(如加法、减法),将存储单元存放的操作数取出送往运算器进行运算,再把结果送回存储器指定的单元中;

第四步:当运算任务完成后,就可以根据指令将结果通过输出设备输出。


  堆栈机制是c语言函数调用的基础,而在linux系统中运行的应用程序通过系统调用来与内核通信。应用程序通常调用库函数(大部分是c库函数)再由库函数通过系统调用界面,让内核代其完成各种不同的任务。


  内核要负责管理系统的硬件设备。现有的几乎所有的体系结构,包括全部Linux支持的体系结构,都提供了中断机制。当硬件设备想和系统通信的时候,它首先要发出一个异步的中断信号去打断处理器的执行,继而去打断内核的执行。内核为我们提供了三种中断机制:软中断、tasklet、工作队列。怎么去选择这些机制,通常软中断是在那些执行频率很高和连续性要求很高的情况下才去使用,而tasklet却有更广泛的用途。tasklet的接口非常简单,而且两个同种类型的tasklet不能同时执行,所以实现起来会简单一些。如果你需要把任务推后到进程上下文中完成,那就只能选择工作队列了。


二、操作系统(内核)是如何工作

    用户界面是操作系统的外在表象,内核才是操作系统的内在核心。系统其他部分必须依靠内核这部分软件提供的服务,像管理硬件设备、分配系统资源等。内核有时候被称作是管理者或者是操作系统的核心。通常一个内核由负责响应中断的中断服务程序,负责管理多个进程从而分享处理器时间的调度程序,负责管理进程地址空间的内存管理程序和网络、进程间通信等系统服务程序共同组成。对于提供保护机制的现代系统来说,内核独立于普通应用程序,它一般处于系统态,拥有受保护的内存空间和访问硬件设备的所有权限。这种系统态和被保护起来的内存空间,统称为内核空间。相对的,应用程序在用户空间执行。它们只能看到允许它们使用的部分资源,并且只使用某些特定的系统功能,不能直接访问硬件,也不能访问内核划给别人的内存范围,还有其他一些使用限制。当内核运行的时候,系统一内核态进入内核空间执行。而执行一个普通用户程序时,系统将以用户态进入用户空间执行。

 

       当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态,比如testfork()最初运行在用户态进程下,当它调用fork()最终触发sys_fork()的执行时,就切换到了内核态。

1. 用户态和内核态的转换

1)用户态切换到内核态的3种方式

a. 系统调用

       这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如前例中fork()实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如Linux的int 80h中断。

b. 异常

       当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。

c. 外围设备的中断

       当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

这3种方式是系统在运行时由用户态转到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的。

2)具体的切换操作

       从触发方式上看,可以认为存在前述3种不同的类型,但是从最终实际完成由用户态到内核态的切换操作上来说,涉及的关键步骤是完全一致的,没有任何区别,都相当于执行了一个中断响应的过程,因为系统调用实际上最终是中断机制实现的,而异常和中断的处理机制基本上也是一致的,关于它们的具体区别这里不再赘述。关于中断处理机制的细节和步骤这里也不做过多分析,涉及到由用户态切换到内核态的步骤主要包括:

[1] 从当前进程的描述符中提取其内核栈的ss0及esp0信息。

[2] 使用ss0和esp0指向的内核栈将当前进程的cs,eip,eflags,ss,esp信息保存起来,这个

过程也完成了由用户栈到内核栈的切换过程,同时保存了被暂停执行的程序的下一

条指令。

[3] 将先前由中断向量检索得到的中断处理程序的cs,eip信息装入相应的寄存器,开始

执行中断处理程序,这时就转到了内核态的程序执行了。

2.中断处理机制的细节和步骤

进入中断和异常:

1,确定与中断或者异常关联的向量i(0~255)

2,读idtr寄存器指向的IDT表中的第i项

3,从gdtr寄存器获得GDT的基地址,并在GDT中查找,以读取IDT表项中的段选择符所标识的段描述符

4,确定中断是由授权的发生源发出的。

 a.中断:中断处理程序的特权不能低于引起中断的程序的特权(对应GDT表项中的DPL vs CS寄存器中的CPL)
 b.编程异常:还需比较CPL与对应IDT表项中的DPL

5,检查是否发生了特权级的变化,一般指是否由用户态陷入了内核态。
如果是由用户态陷入了内核态,控制单元必须开始使用与新的特权级相关的堆栈

a,读tr寄存器,访问运行进程的tss段

b,用与新特权级相关的栈段和栈指针装载ss和esp寄存器。这些值可以在进程的tss段中找到

c,在新的栈中保存ss和esp以前的值,这些值指明了与旧特权级相关的栈的逻辑地址

6,若发生的是故障,用引起异常的指令地址修改cs和eip寄存器的值,以使得这条指令在异常处理结束后能被再次执行

7,在栈中保存eflags、cs和eip的内容

8,如果异常产生一个硬件出错码,则将它保存在栈中

9,装载cs和eip寄存器,其值分别是IDT表中第i项门描述符的段选择符和偏移量字段。这对寄存器值给出中断或者异常处理程序的第一条指定的逻辑地址


从中断和异常返回

   中断/异常处理完后,相应的处理程序会执行一条iret汇编指令,这条汇编指令让CPU控制单元做如下事情:

1,用保存在栈中的值装载cs、eip和eflags寄存器。如果一个硬件出错码曾被压入栈中,那么弹出这个硬件出错码

2,检查处理程序的特权级是否等于cs中最低两位的值(这意味着进程在被中断的时候是运行在内核态还是用户态)。若是,iret终止执行;否则,转入3

3,从栈中装载ss和esp寄存器。这步意味着返回到与旧特权级相关的栈

4,检查ds、es、fs和gs段寄存器的内容,如果其中一个寄存器包含的选择符是一个段描述符,并且特权级比当前特权级高,则清除相应的寄存器。这么做是防止怀有恶意的用户程序利用这些寄存器访问内核空间。


参考:http://jakielong.iteye.com/blog/771663





 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值