点滴汇聚

不积跬步无以至千里,不积小流无以成江海

为了学习了解嵌入式虚拟化的技术,记录一下自己折腾的经历以及搭建验证开发环境的坑。

阅读全文 »

CFS是内核使用的一种调度器或调度类,它主要负责处理三种调度策略:SCHED_NORMAL、SCHED_BATCH和SCHED_IDLE。从上一篇文章 可知,调度器的核心在挑选下一个运行的进程时有可能会遍历所有的调度类别。实际上系统大多数进程通常都是CFS调度类负责处理的,因此为了优化下一个进程的挑选调度器核心会先判断当前进程是否采用了CFS调度策略,若是,则直接调用CFS代码来挑选下一个进程,若不是或CFS代码未能挑选到一个合适的进程,则会调用各个调度类的挑选函数来寻找一个合适的进程。若CFS代码寻找到了合适的下一个运行的进程,则直接返回该进程的实例而不会再遍历调度类。本文将主要关注下列的CFS活动和行为:

  • 将一个任务插入运行队列
  • 从运行队列中挑选一个合适的任务
  • 从运行队列中移除一个任务
  • 处理周期滴答「periodic ticks」
  • 管理调度实体「scheduling entities」的运行时间
  • 管理时间片「time slices」
阅读全文 »

Linux内核是一个支持多任务「multitasking」的操作系统,可以同时执行多个任务,但是CPU在某个时刻只能运行一个任务,因此为了使Linux内核真的看起来像多个任务在同时运行,调度器将连续不断地替换当前正在运行的任务,这个替换的过程被称为任务切换「task switch」或者调度「scheduling」。
内核通过处理定时器「timer」中断能够周期循环地检查当前进程的need_resched标志是否设置以及时间片「time slice」是否用尽,若当前进程设置了need_resched标志或者用完了时间片,那么将发生任务切换,这个过程就是利用定时器「timer」中断处理函数实现的周期调度「periodic scheduling 」。除此之外,内核还有很多其他代码会随时检查当前进程的need_resched标志是否设置,若need_resched标志被设置了,则将进行任务切换,这个过程属于非周期调度。在内核中这两种调度能够保证调度器尽可能经常地进行任务切换,以致于所有现存的进程能够尽可能公平地被执行。与周期调度不同,内核常见的非周期调度大致可分两种:被动式非周期调度和主动式非周期调度,比如如下所述的两个场景:

  • 进程相关事件发生时会调用设置need_resched标志的函数,比如一个进程的状态发生了变化(进程唤醒事件出现、进程优先级改变)或者当前进程主动让出「yield」CPU执行时间,这只是触发了调度请求「scheduling request」,而真正的任务切换却发生于调用schedule()的函数,或检测need_resched标志是否设置的函数。
  • 与前者不同的是有些代码(sleep、blocking API、lock API)会使当前进程进入睡眠状而不能继续执行,则会直接显式调用核心调度函数__schedule()进行任务切换。

下面给出了触发任务切换的调度点「scheduling point」,它们将检查need_resched标志是否被设置,如果设置的话,则会调用核心调度函数进行任务切换。

  • 在中断处理完成后
  • 在系统调用处理完成后
  • 在使能内核抢占后

总之,本文主要分析使用timer中断实现的周期调度、非周期调度涉及的调度请求「scheduling request」以及任务切换。

阅读全文 »

本文主要关注内核中用于调度进程的各种机制,以及为了实现这些机制而引入的各种概念和数据结构。首先了解调度器「scheduler」的如下关键概念,有了这些基础才能深入理解调度器的各种不同活动和行为。

  • 刻画进程重要性的负载权重「load weight」
  • 进程的虚拟运行时间「virtual runtime」
  • 进程时间片「time slice」和CFS运行队列的调度延迟「scheduling latency」
  • 运行队列「run queue」、CFS运行队列和红黑树「Red Black Tree」
  • 组调度「group scheduling」涉及的任务组「task group」和调度实体「scheduling entity」
  • 调度类别「scheduling class」和调度策略「scheduling policy」

后续第二篇文章会分析调度器的核心部分,也就是常用的调度代码。涉及的知识点如下:

  • 基于定时器「timer」中断的周期调度「periodic scheduling 」
  • 非周期调度「aperiodic scheduling」
  • 触发任务切换的调度点「scheduling points」
  • 设置need_resched标志的调度请求「scheduling request」
  • 调度核心函数__schedule()
  • 进程唤醒函数

在研究完调度器核心之后,最后一篇文章将分析CFS「Completely Fair Scheduling)」调度器,它负责处理SCHED_NORMAL、SCHED_BATCH和SCHED_IDLE三种调度策略。涉及的内容如下:

  • 将一个进程插入运行队列
  • 从运行队列中挑选一个合适的进程
  • 从运行队列中移除一个进程
  • 处理周期滴答「periodic ticks」
  • 管理调度实体「scheduling entities」的运行时间
  • 管理时间片「time slices」

本文在最后会分析sched_init()函数,它会完成调度器正常工作所要求的初始化任务。

阅读全文 »

为了更深入地剖析kernel,了解ARMv8体系架构是非常必要的。本文尽量全面地概述ARMv8体系架构涉及的基础知识。Linux内核的绝大部分代码是与体系架构无关的「independent」,但是还存在大量的代码「spinlock、cache管理」都是与体系架构相关的「dependent」,这些代码不仅会在启动时被运行而且在系统正常运行期间也会被经常调用执行。不仅仅Linux内核牵涉了很多的体系架构知识,而且编译器(比如LLVM「Low Level Virtual Machine」)以及与内核紧密相关的领域(比如虚拟机)也都涉及了大量体系架构方面的知识,为了使系统或软件最大地利用硬件的特点和性能,学习了解体系架构的知识就显得格外重要。尽管ARMv8设备已被广泛地应用到各个领域,但是关于ARMv8体系架构的书籍不是太多,还比较零散,因此我希望本文能够尽量全面的介绍ARMv8体系架构方面的知识点,使我们在研究Linux内核代码不会因为体系架构知识短缺而困惑。

阅读全文 »

Exynos4412 芯片提供的 fwbl.bin只能加载 16KiB 大小的 u-boot-spl.bin到内部 SDRAM,通常编译得到的u-boot.bin远远超过这个限制,因此使用u-boot源代码支持的SPL功能来解决该问题 。不过,u-boot-spl.bin与u-boot.bin共用了很多基础代码。CONFIG_SPL_BUILD选项控制着SPL功能的开关,若在配置文件中启用了该选项,在编译时会在spl子目录下构建出SPL相关的代码文件树。同时它还会把整个启动引导过程分为两个阶段,u-boot-spl.bin完成第一阶段的初始化设置,主要是初始化 SoC时钟、外部内存DDR和早期串口调试功能,最后加载第二阶段涉及的u-boot.bin到外部内存并跳转执行。

阅读全文 »

percpu变量指的是为每个CPU都创建一个同样的变量,也就是为每个CPU分配一份单独的内存空间,用来保存各自私有的数据。在SMP「Symmetry Multi-Processor」系统中,为了确保多个CPU能够正确地访问同一个资源,同步机制是必不可少的,比如缓存一致性「cache coherence」和自旋锁「spinlock」。但是通过使用percpu变量,内核能够使每个CPU只访问自己私有的内存空间,因此可以避免使用同步机制「free-lock」。本文将逐步揭开percpu内存管理机制的面纱。

阅读全文 »
0%