Kernel

Linux进程内核级讲解

在用户空间,我们在大学里必学的内容肯定包括进程和线程。
进程,是一个正在运行的程序,以及它所占用的资源。
在Linux中,我们只需要讨论进程,而不用单独学习线程。因为,Linux中的线程,仅仅是一种“特殊”的进程。二者的差异微乎其微,以至于我们仅需要在遇到时一笔带过。

在大学课程中,老师必定教过:Unix-like操作系统开机过程中,会创建一个太太太太太爷爷级别的进程。这个进程一般称为init进程,pid设为1。除此之外,一切进程都是init进程的后代。init进程创建后代的方式只有1个:fork()函数。

理论上init进程可以做任何事情。不过它的首要任务显然是fork一堆孩子出来,进一步完成开机过程中操作系统的初始化工作。当你有一堆相似的东西,并且它们构成一棵树,数据结构和算法课程学到的知识,就必然会派上用场。
我们要设计一个数据结构,它可以

  1. 抽象地表示一个进程。
  2. [高效率地]实现遍历所有进程,并支持添加、删除等操作。
  3. 考虑到进程无法同时运行,我们需要设计一个方案,让它们在有限的处理器资源下和谐相处。

实现上述目标有很多种解决方案,有些很显然是愚蠢的设计,有些则互有优缺点。Linux的解决方案很棒,但不是唯一最优方案。它一直在不断改进,有很多人在进程管理子系统上努力。

Linux进程的抽象——task_struct

Linux内核开发中,进程process,也经常被称为任务task。进程的数据结构,命名为task_struct。作为一棵树的叶子节点,它的内容是比较无聊的,无非是一个进程各方面属性的完整清单。
源码:https://elixir.bootlin.com/linux/latest/ident/task_struct
我会在另一篇文章中逐行解读它的全部内容,但现在,我们只需要专注于少数几个属性。
task_struct的实例,被称为process descriptor进程描述符。

Linux进程集合的抽象——task list

Linux并没有单独设计一个数据结构来表示所有进程组成的集合,相反,它设计了一个通用的数据结构,然后仅仅是让task_struct符合这个通用数据结构。如此一来,Linux只需要使用对应的通用算法,就可以实现对所有进程的遍历。双向环形链表,是当前版本Linux的选择。

不可能实现的完全公平调度器

CFS Completely Fair Scheduler
源码:https://elixir.bootlin.com/linux/latest/source/kernel/sched/fair.c
源码解读:待补充

CFS调度入口