# 2.1 任务

进程是现代操作系统中最重要的概念之一，一个进程表示程序的一个执行实例。进程本身也是一个动态的概念，代表着程序一次从头到尾的执行过程，因此进程有自己的生命周期，可能处于不同的状态，这方面的资料已经非常丰富，就不在这里赘述了，感兴趣的同学[上网搜](https://www.google.com/search?q=process+state+diagram)就可以了。

然而在Linux中，进程并不是最小的执行单元，系统中最小的执行单元是线程（Thread）。但在内核层面并没有对进程与线程做显示的区分，二者统一都叫着任务（Task），区别体现在对资源的共享程度上：进程本质上是对资源的一种抽象与隔离，包括内存地址空间、文件、信号量等，Linux在通过系统调用 `clone` 创建一个新的 task 时，如果所有的资源都不共享，那么对用户而言就是创建了一个新的进程，如果除了函数调用栈（Stack）不共享、其他所有资源都共享，则创建的便是一个线程。

Linux 使用 `task_struct` 来表示一个任务，其定义如下：

```
/* file: include/linux/sched.h */
struct task_struct {
    /* 与优先级相关的字段 */
    int prio;
    int static_prio;
    int normal_prio;

    unsigned int rt_priority;

    /* 调度策略 */
    const struct sched_class *sched_class;
    /* 调度实体，调度器的调度对象，该字段用于 CFS */
    struct sched_entity se;
    /* 该字段用于 RT 调度器 */
    struct sched_rt_entity rt;
#ifdef CONFIG_CGROUP_SCHED
    struct task_group *sched_task_group;
#endif
    /* 该字段用于 DL 调度器 */
    struct sched_dl_entity dl;
}
```

`task_struct` 是一个非常大的结构体，包含了进程运行时的所有信息，这里我们只罗列出了几个与调度逻辑相关的字段，这些字段的作用在本章的后续章节中会做进一步分析。

从调度的角度来看，不同类型进程的调度需求是不一样的：

* 交互式进程（Interactive Process） 进程运行过程中需要不断通过 I/O 与用户交互，由于用户行为的速度与CPU的执行的速度有巨大落差，所以在 OS 看来这类进程大多数时间都在等待I/O事件。

  为了提升用户体验，该类进程最好能够有较好的响应时间，即一旦用户完成了操作，我们希望进程能够以最快地速度完成响应。所以对于调度器而言，如果这类进程是就绪的，那么最好立刻执行他们。也就是说，这类进程应该具备较高的优先级。
* 批处理进程（Batch Process） 这类进程几乎没有交互需求，只要他们在后台默默运行就好。用户对他们的期望是有足够大的吞吐量，因此每次被调度到时，最好都能稳稳当当运行一段时间，以便能够更好的利用CPU缓存以提升效率。
* 实时进程（Real-time Process） 这类进程对响应时间要求极高，因此一旦进程处于就绪状态，就需要立刻扔到 CPU 上执行。

在接下来的章节中，我们将详细探讨Linux对进程的调度原理，弄清楚内核如何区分不同类型的进程，并着重分析CFS调度期的实现细节。
