标签:不同的 创建 设备 结构 先进先出 学习 优先级 一个 内核
程序员编写的程序要想获得运行,必须首先把静态的程序变成一个个动态的进程,进程因创建而产生,因调度而执行,因撤销而消亡,这便是一个进程的一个生命周期。在电脑的内存中,有着成千上万的进程,而cpu仅仅只有一个,那该如何管理这些进程完成进程的调度呢?
首先从进程的管理谈起,进程由三部分构成——进程控制块PCB,数据段和代码段,操作系统正是通过管理进程的PCB来实现管理进程的。进程的PCB本质上是一个结构体数组,包含有这个进程的所有信息,操作系统可以对外提供相应的函数去改变进程PCB中相应的域。进程的调度和PCB中的很多域相关,比如关于时间片计数器counter,进程优先级priority,进程调度策略policy以及指向可运行队列的下一个和前一个PCB的指针*next_run和*prev_run.
linux进程的调度依赖于一个数据结构——可运行队列,本质上也是一个结构体数组,其组成结构如下图所示:
先来解释下这个可运行队列,可运行队列包含有活动进程可运行队列active和过期可运行队列expired,他们分别指向一个prio_array_t结构体类型变量的地址,这个prio_array_t结构体包含有三部分,分别是代表队列中PCB数量的nr_active,代表每一个优先级队列中是否有PCB的bitmap,代表140个优先级队列的queue。
linux的进程调度算法具有优先级、时间片、可剥夺、先来先服务四大特点。下面依次来谈。
实时进程和普通进程采取的linux调度算法是不同的。
有了上面知识的铺垫,接下来谈谈具体的linux进程调度过程:
linux操作系统除了能够正常的调度进程的执行外,还应该具备有响应中断事件的能力。为了解决cpu的高速运行和I/O设备的低速处理之间的矛盾,linux提供了中断机制:当对外部设备发出读或者写命令后,cpu不用等待外部设备准备数据,而是继续调度执行就绪进程,只有当外部设备准备好数据后,才会向cpu发出中断信号,cpu此时就会改变程序流,立即去响应并处理中断事件,过程如图所示:
在linux中,每个中断和异常由0~255之间的一个数(8位)来标识,称之为中断向量。其中0~31号的中断向量分配给了异常处理,128号中断向量分配给了系统调用,剩下的中断向量分配给了外部中断。中断和异常是广义上中断的两个分类,此时狭义上的中断指的是外部中断,由硬件触发的中断,比如时钟中断,I/O中断等。异常是由软件触发的中断,比如缺页异常,除0异常等。
linux处理外部中断过程如下:
在每一条指令执行周期结束后cpu都会查看cpu的INTR引脚上是否有中断信号,如果电平发生变化,说明有中断产生;
cpu读取「中断控制器」中数据端口中的「中断向量」;
cpu读取「IDTR寄存器」,找到「中断描述符表IDT」,根据中断向量号找到相应的表项;
根据表项中相关位上的数据进行安全检查,查看此次中断是否合法;
检查完后,若合法,进行硬件级别的保护现场工作。
关中断
将ss,esp,eflags,cs,eip寄存器中的值依次进入「被中断进程的内核栈」。
开中断
进行软件级别的保护现场工作
调用do_IRQ函数,执行中「断处理程序」;
执行ret_from_intr函数,进行中断返回和恢复现场。
linux处理异常的过程如下:
当发生异常时,进行硬件级别的保护现场
关中断
将ss,esp,eflags,cs,eip寄存器中的值依次进入「被中断进程的内核栈」。
开中断
将出错码入栈和相应的c函数地址入栈
执行error_code函数,按照pt_regs结构进一步保存现场
把堆栈地址中的do_handler_name()函数的地址装入edi寄存器,并在这个位置写入fs值
执行call *%edi指令
执行ret_from_exception函数,进行中断返回和恢复现场。
在进行异常和中断处理时,保护现场时的内核栈情况如下入所示:
标签:不同的 创建 设备 结构 先进先出 学习 优先级 一个 内核
原文地址:https://www.cnblogs.com/Luck-365/p/13276086.html