三言两语聊kernel:调度入门

前言

前面说了内存管理入门, 内存管理是理解linux内核的基础, 如果不了解内存管理,就无法深入了解内核,理解了内存管理,再学习内核的其他部分就会相对很容易。

这次我要说的是调度这部分, 调度在嵌入式系统中也是一个很重要的方面,众所周知,嵌入式系统对实时性要求较高, 实时性本身就是一个调度问题。

进程调度的目的

linux进程调度是为了支持多进程,如果只是单一进程,显然是不需要调度的。 在对多进程的支持过程中, 如何合理的分配系统资源是进程调度要解决的问题,主要包括cpu该选择哪一个进程执行以及让该进程一次执行多长时间,另外一个就是smp系统的负载均衡, 即如何提高多核系统的并行执行性能。

进程调度是针对task_running状态的进程进行调度, 如果进程不处于task_running状态,进程调度跟它就是没有关系的。 于是就引入了进程状态这个概念。

进程状态

进程状态分为:

  • task_running状态 task_running状态就是可以被调度的状态,内核在调度点都是选择运行队列上的一个进程来执行。
  • 睡眠状态 睡眠状态分为两种:不可中断状态(R状态)和可中断状态(S状态),一般是进程在运行状态想获得某一个资源但是暂时又得不到,那么该进程就会进入睡眠状态,在条件成立的时候再唤醒该进程,唤醒的方式一般都是通过wake_up系列函数来唤醒。 对于S状态的进程,它还可以被信号给唤醒。
  • task_stop状态 task_stop状态是指正在运行的状态收到了sig_stop等一些信号而进入暂停状态, 这可以通过sig_continue信号来将其唤醒。
  • 僵死状态。 当进程已经停止运行,但是其父进程还没有读取该子进程的exit状态时,它的task_struct就会驻留在内存中形成了僵死进程。
    • 僵死进程的解决方法是 一是让父进程来waitpid来接管sigchld信号
      二是 结束它的父进程,让init进程来处理。
    • 产生的原因
      如果父进程fork子进程时没有处理sigchld这个信号, 就会形成僵死进程,有时候可能网络原因等也会产生僵死进程。

进程调度点

进程有用户态和内核态这两个状态, 即它有两个栈空间,分别时用户栈空间和内核栈空间。 在内核态下和用户态下都可以发生调度,内核态的调度发生在从中断上下文返回到内核态之前(即进程被中断打断了,中断处理完要返回的时候), 用户态的调度发生在从内核态返回到用户态的时候(即从系统调用返回的时候。)。  内核态的调度需要打开内核抢占,如果不支持内核抢占,在内核态是不会发生调度的。

ok, 接下来要搞清这个问题。假如我一个进程正在处于用户态,突然来了个中断,会发生什么? 这个时候,cpu会从特权级3进入特权级0, 进程会由用户态切换到内核态(即从用户态的栈切换到内核态的栈),用户态堆栈指针会压入内核堆栈,中断执行完,首先返回到内核态,然后在从内核态返回到用户态的时候会恢复用户态堆栈。

进程的调度策略

进程按照其优先级分为实时进程和普通进程。

实时进程的优先级较高, 实时进程又有两种调度策略:一个时FIFO,另一个是RR。对于FIFO进程而言,只有它执行完,才会选择其他进程执行(没有流控的情况下),而对于RR进程, 则是时间片轮转策略。

抢占

linux内核抢占需要在menuconfig里面打开config_preempt选项。

用户抢占则是天生就可以的, 如果不可以, 那怎么支持多进程涅:)

从用户态进入内核态的情况

  • 系统调用
  • 异常指令(其实系统调用也是一个异常指令)
  • 中断

Comments