进程的切换和系统的一般执行过程

中断

中断在本质上都是软件或者硬件发生了某种情形而通知处理器的行为,处理器进而停止正在运行的指令流(当前进程),对这些通知做出相应反应,即转去执行预定义的中断处理程序(内核代码)。

中断分为硬中断软中断

硬中断就是CPU的两根引脚(可屏蔽中断和不可屏蔽中断),CPU在执行每条指令后会检测这两根引脚的电平,如果是高电平,说明有中断请求,CPU就会中断当前程序的执行去处理中断。

软中断包括除零错误,系统调用、调试断点等在CPU执行指令过程中发生的各种特殊情况统称为异常。异常一般分为3种,故障,退出,陷阱。区别是故障可以恢复到当前指令,退出是不可恢复的严重故障,陷阱是程序主动产生的异常,例如int 0x80。

进程调度的时机

linux内核通过schedule函数实现进程调度,schedule函数在运行队列中找到一个进程,把CPU分配给它。调用schedule函数一次就是调度一次。调用schedule函数的时候就是进程调度的时机。

调用schedule函数有两种方法:

  • 进程主动调用schedule()
  • 松散调用

进程调度时机如下:

  1. 用户进程通过特定的系统调用主动让出CPU
  2. 中断处理程序在内核返回用户态时进行调度
  3. 内核线程主动调用schedule函数让出CPU
  4. 中断处理程序主动调用schedule函数让出CPU,涵盖第一和第二种情况

Linux内核中没有操作系统中定义的线程概念,从内核角度看,不管是进程还是内核线程都对应一个task_struct数据结构,本质上都是进程,linux系统在用户态实现的线程库pthread是通过在内核中多个进程共享一个地址空间实现的。

一般来说,cpu在任何时刻都处于以下3种情况之一:

  1. 运行于用户空间,执行用户进程上下文
  2. 运行于内核空间,处于进程(一般是内核线程)上下文
  3. 运行于内核空间,处于中断上下文。

内核线程以进程上下文的形式运行在内核空间中,本质上还是进程,但是它还有调用内核代码的权限,比如主动调用schedule函数让出cpu等

调度策略与算法

调度策略要考虑这个算法的整体目标,是追求资源利用率最高还是追求响应最及时,或是其他的一些目标,然后找到对应的方法或机制作为对策,这就是调度策略。

调度算法就是从就绪队列中选取一个进程,考虑如何实现调度策略并满足设定的目标

进程的分类1:
I/O消耗型进程
处理器消耗型进程 进程的分类2:
交互式进程
批处理进程
实时进程

调度策略:

#define SCHED_NORMAL 0 //普通进程 使用CFS调度管理程序
#define SCHED_FIFO 1//实时进程 优先级高于第一个
#define SCHED_RR 2 //实时进程 优先级高于第一个
#define SCHED_BATCH 3//保留,未实现
#define SCHED——IDLE 5//idle进程

内核中根据进程的优先级来区分普通进程与实时进程,Linux内核进程优先级为0-139,数值越高,优先级越低,0位最高优先级,实时进程的优先级取值为0-99,而普通进程只有nice值,范围为100-139,子进程会继承父进程的优先级

CFS调度算法:

CFS即为完全公平调度算法,其基本原理是基于权重的动态优先级调度算法。每个进程使用CPU的顺序由进程已使用的CPU虚拟时间(vruntime)决定,已使用的虚拟时间越少,进程排序越靠前,进程在此被调度执行的概率也就越高。

进程上下文切换

为了控制进程的执行,内核必须有能力挂起正在CPU中运行的进程,并恢复执行以前挂起的某个进程,这种行为被称为进程切换、任务切换或进程上下文切换。

进程上下文包含了进程执行需要的所有信息

  • 用户地址空间
  • 控制信息
  • 硬件上下文
  • CR3寄存器
  • ESP寄存器
  • EIP寄存器及其他寄存器

linux操作系统内核为用户提供的服务:

  • 通过系统调用的形式为进程提供各种服务
  • 通过异常处理程序与中断服务程序为硬件的正常工作提供各种服务
  • 通过内核线程为系统提供动态的维护服务和中断服务中可延时处理的任务

linux系统的一般执行过程##

情景:正在运行的用户态进程X切换到用户态进程Y的过程

  1. 正在运行的用户态进程X
  2. 发生中断(包括异常、系统调用等),硬件完成以下动作
    • save cs:eip/ss:esp/eflags:当前CPU上下文压入用户态进程X的内核堆栈
    • load cs:eip and ss:esp 加载当前进程内核堆栈相关信息,跳转到中断处理程序,即中断执行路径的起点。
  3. SAVE_ALL 保存现场,此时完成了中断上下文切换,即从进程X的用户态到进程X的内核态
  4. 中断处理过程中或中断返回前调用了schedule函数,其中的switch_to做了关键的进程上下文切换
  5. $1f后开始运行用户态进程Y
  6. restore_all 恢复现场
  7. iret-pop cs:eip/ss:esp/eflags从Y进程的内核堆栈中弹出2中硬件完成的压栈内容
  8. 继续运行用户态进程Y

Linux操作系统的整体构架##

实验:进程调度相关源代码跟踪和分析##

概述:本次实验是使用gdb跟踪分析schedule函数,总共设置了4个断点,schedule,context_switch,switch_to,pick_next_task.

大致逻辑是,要发生进程调度时,首先会调用schedule函数,而schedule函数里有一个pick_next_task函数,字面意思是选择下一个进程,该函数负责根据调度策略和调度算法选择下一个进程,而context_switch函数也属于schedule函数,该函数用于实现进程切换,switch_to则属于context_switch函数,进行进程关键上下文切换。调试截图如下。

schedule



pick_next_task



context_switch