2018-2019-1 20189205 《Linux内核原理与分析》 第九周作业

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

进程调度的时机

Linux内核通过schedule函数来实现进程调度。每次调用schedule函数,其都会在进程队列中查找一个进程,将CPU分配给它。
调用schedule函数的方法

  • 进程主动调用schedule函数,如进程调用阻塞的系统调用等待外设或主动休眠,最终会在内核中调用到schedule函数。
  • 松散调用,内核代码中可以随时调用schedule函数使当前内核路径让出CPU;也会根据need_resched标记做进程调度,内核会检测need_resched决定是否调用schedule函数。

调用schedule函数的时机
内核调用schedule函数的时机可以简单归结为如下过程:

  • 用户进程通过特定的系统调用主动让出CPU;
  • 中断处理程序在内核返回用户态时进行调度;
  • 内核线程主动调用schedule函数让出CPU;
  • 中断处理程序主动调用schedule函数让出CPU(包括以上两点)。

进程调度策略

考虑算法的整体目标,选择可以满足这个目标所的方法或机制作为对策,即为调度策略。
Linux系统中常用的调度策略有以下三种:

  • SCHED_FIFO 先进先出的实时进程,如果没有其它更高优先级的可运行实时进程,就可以一直使用cpu运行。对于这种进程,时间片长度是没有意义的。
  • SCHED_RR 时间片轮转的实时进程,所具有相同优先级(且都是当前情况下优先级最高)的SCHED_RR以时间片轮转的方式公平使用cpu。
  • SCHED_NORMAL 时间片轮转的普通进程,时间片用完之后变成过期进程,所有进程都成为过期进程之后,再统一把过期进程转变为活动进程。
    其中前两种为实时进程,优先级取值为099;最后一种为普通进程,只有映射到优先级100139的nice值。

进程调度算法

Linux系统的进程调度算法采用CFS即完全公平调度算法,其原理是基于权重的动态优先级调度算法。即:

  • 每个进程使用CPU的顺序由进程已使用的CPU虚拟时间(vrumtime)决定,已使用的虚拟时间越少,进程排序越靠前,进程被在此调度执行的概率越高;
  • 每个进程每次占用CPU后可执行的运行时间(ideal_runtime)由进程的权重决定;
  • 保证每个时间周期(__sched_period)内运行队列中的所有进程都至少被调度一次。

其中:

__sched_period = nr_running(进程数)* sysctl_sched_min_granularity(默认值为0.75ms);
ideal_runtime = __sched_period * 进程权重 / 运行队列总权重 ;
vrumtime += delta_exec(本次CPU占用时间)* NICE_0_lOAD (nice值为0的进程的权重) / se->load.weight (自身权重);

进程上下文

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

  • 用户地址空间:包括程序代码,数据,用户堆栈等;
  • 控制信息:进程描述符,内核堆栈等;
  • 硬件上下文:相关寄存器的值。
    进程切换就是变更进程上下文。

代码跟踪分析

配置断点:

schedule()函数上下文

pick_next_task函数上下文

context_switch函数上下文

问题分析

对于书中有关vruntime的部分:

在进行计算时,书中的代码存在一些问题。

  • 第一行的判断条件应当是if se->load.weight == NICE_0_LOAD 因为 if 部分计算的是nice值为0的进程的 vruntime值,因此判断条件应为==,而非!=
  • 最后一行else部分的代码应当是vrumtime+= delta_exec *NICE_0_lOAD/se->load.weight

另外在之后的论述中:

  • 虚拟时间等于实际执行的物理时间的进程不应当是0优先级的进程,而是nice值为0的进程。
  • 检查是否需要设置的调度标志是need_resched而非need_schedule
原文地址:https://www.cnblogs.com/hzj20189205/p/10093116.html