1、前言
本文主要基于Linux Kernel 2.6.32 的源码,对Linux的进程模型进行分析,大致可以概括为如下内容:
1.前言
2.进程介绍
3.操作系统如何组织进程
4.进程状态的转化
5.进程的调度
6.参考资料
2、进程介绍
2.1 进程的概念
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
2.2 进程的查看
2.2.1 Windows查看进程
在Windows系统下可用CTRL+ALT+DEL组合键打开任务管理器对进程进行查看:
2.2.2 Linux下查看进程
Linux下可以在终端用ps命令来查看进程:
3、操作系统如何组织进程
3.1 进程控制块(PCB)
Linux系统中主要的活动实体就是进程。
每个进程执行一段独立的程序并且在进程初始化的时候拥有一个独立的控制线程。换句话说,每一个进程都拥有一个独立的程序计数器,用这个这个程序计数器可以追踪下一条将要被执行的指令。
所有的进程都被放在一个叫做进程控制块(PCB),的数据结构中,可以理解为进程属性的集合,该控制块由操作系统创建和管理。每个进程在内核中都有一个进程控制块来维护进程相关的信息,Linux内核的进程控制块是(task_struct)结构体。Linux通过task_struct(PCB)结构体来描述一个进程的所有信息,结构体被定义在include/linux/sched.h中。
进程创建时,操作系统就新建一个PCB结构,它之后就常驻内存,任一时刻可以存取, 在进程结束时删除。PCB是进程实体的一部分,操作系统通过PCB表来管理和控制进程,是进程存在的唯一标志。
3.2 进程标识符(PID)
进程标识符在task_struct结构体下定义:
pid_t pid; //内核中用以标识进程的id pid_t tgid; //用来实现线程机制 struct pid { atomic_t count; unsigned int level; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; struct rcu_head rcu; struct upid numbers[1]; };
每个进程都有一个唯一的标识符(PID),内核通过这个标识符来识别不同的进程,同时,进程标识符(PID)也是内核提供给用户程序的接口,用户程序通过PID对进程发号施令。PID是32位的无符号整数,它被顺序编号:新创建进程的PID通常是前一个进程的PID加1。然而,为了与16位硬件平台的传统Linux系统保持兼容,在Linux上允许的最大PID号是32767,当内核在系统中创建第32768个进程时,就必须重新开始使用已闲置的PID号。在64位系统中,PID可扩展到4194303。
4、进程状态的转化
4.1 三态模型
一个进程从创建而产生至撤销而消亡的整个生命周期,可以用一组状态加以刻划,根据三态模型,进程的生命周期可分为如下三种进程状态:
1. 运行态(running):占有处理器正在运行
2. 就绪态(ready):具备运行条件,等待系统分配处理器以便运行
3. 等待态(blocked):不具备运行条件,正在等待某个事件的完成
4.2 五态模型
在一个实际的系统里进程的状态及其转换比上节叙述的会复杂一些,例如引入专门的新建态(new)和终止态(exit )
状态转换图如下所示:
5、进程的调度
5.1 linux调度器的演变
当前的内核支持两种调度器类(sched_setscheduler系统调用可修改进程的策略):CFS(公平)、RT(实时);5种调度策略:SCHED_NORAML(最常见的策略)、SCHED_BATCH(除了不能抢占外与常规任务一样,允许任务运行更长时间,更好地使用高速缓存,适合于成批处理的工作)、SCHED_IDLE(它甚至比nice 19还有弱,为了避免优先级反转使用)和SCHED_RR(循环调度,拥有时间片,结束后放在队列末)、SCHED_FIFO(没有时间片,可以运行任意长的时间);其中前面三种策略使用的是cfs调度器类,后面两种使用rt调度器类。
5.2 CFS调度器的结构
第一个是调度实体sched_entity,它代表一个调度单位,在组调度关闭的时候可以把他等同为进程。每一个task_struct中都有一个sched_entity,进程的vruntime和权重都保存在这个结构中。那么所有的sched_entity怎么组织在一起呢?红黑树。所有的sched_entity以vruntime为key(实际上是以vruntime-min_vruntime为单位,难道是防止溢出?反正结果是一样的)插入到红黑树中,同时缓存树的最左侧 节点,也就是vruntime最小的节点,这样可以迅速选中vruntime最小的进程。注意只有等待CPU的就绪态进程在这棵树上,睡眠进程和正在运行的进程都不在树上。如下图(来源见参考资料):
6、参考资料
1. https://baike.baidu.com/item/%E8%BF%9B%E7%A8%8B/382503?fr=aladdin
2. https://baike.baidu.com/item/%E8%B0%83%E5%BA%A6%E7%AE%97%E6%B3%95/3017645?fr=aladdin
3. https://www.cnblogs.com/puputongtong/p/5451210.html
4. https://blog.csdn.net/gatieme/article/details/52067518