标签:动态 数据 内容 cfs 有关 9.png 关系 mic 因此
本文主要基于Linux Kernel Version 4.13.0-36-generic的源代码,来进行深入分析其进程模型,具体包含的内容如下:
1. 操作系统是怎么组织进程的
2. 进程状态如何转换
3. 进程是如何调度的
4. 自己对该操作系统进程模型的看法
(注:Linux Kernel Version 4.13.0-36-generic源代码的连接地址:https://elixir.bootlin.com/linux/v4.13/source/kernel)
1).进程是对正在运行程序的一个抽象。一个进程就是一个正在执行程序的实例,包括程序计数器、寄存器和变量的当前值。
2).狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
3).广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
与其在进程的定义上纠结,不如亲自去实践。
在Windows操作系统上,我们可以通过打开任务管理器,查看各类进程。
在Linux操作系统上,我们可以在终端命令行输入ps aux并回车查看所有进程。
其中各个字段的解释:
(PID:进程ID;%CPU:CPU占有数;%MEM:物理内存;VSZ:虚拟内存;RSS:实际物理内存;STAT:进程状态;START:启动时间;COMMAND:进程名称)
task_struct是Linux内核的一种数据结构,它保存进程的信息,可以在include/linux/sched.h中找到它,所有系统的进程都是以task_struct链表的形式存在在内核中的。
现在介绍一下task_struct中包含的一些主要内容:
PID:为进程的标识
Processor:标识用户正在使用的CPU
State:标识进程的状态(有六种,见后文介绍)
Prority:实时进程的优先级,对普通进程无效
Policy:表示进程调度策略
进程标识符PID是描述本进程的唯一标识符,来区别其他进程。它在task_struct中的定义为如下:
对于PID的取值范围,在include/linux/threads.h中,有如下宏定义:
在CONFIG_BASE_SMALL配置为0的情况下,PID的取值范围是0~32767,所有系统中的进程数最大值为32768个。
在task_struct中定义如下:
State成员的可能取值如下:
现在介绍一下Linux中的进程的几个状态:
1. 可运行状态(TASK-RUNNING):表示进程要么正在执行,要么正要准备执行。
2. 可中断阻塞状态(TASK-UBERRUPTIBLE):表示进程被阻塞,直到某个条件为真。如果条件达成,则状态变为可运行态。
3. 不可中断阻塞状态(TASK-UN INTERRUPTIBLE):不能通过接受一个信号来唤醒。
4. 僵死状态(TASK-ZOMBLE):表示进程的执行被终止,但是父进程还没有使用wait()等系统调用来获知它的终止信息。
5. 暂停态(TASK-STOPPED):表示进程被停止执行
进程的基本状态如下图:
优先级在task_struct中定义如下:
在操作系统中,完成选择工作的这一部分就称为是调度程序,该程序使用的算法称为调度算法。根据如何处理时钟中断,可以调度算法把调度算法分为两类:非抢占式和抢占式。不同的环境需要不同的调度算法,换句话说,在不同的系统中,调度程序的优化是不同的,所以这里有必要划分三种环境:批处理、交互式和实时。
典型的调度算法有:
(1) 先来先服务
(2) 短作业优先
(3) 优先级
(4) 高响应比优先
(5) 时间片轮转
在这里,我想来介绍一下CFS(Completely Fair Scheduler)调度器,即完全公平调度器。
CFS是在Linux Kernel 2.6.23之后采用,使用红黑树来实现,算法效率为O(log(n))。
调度算法调度算法最核心的两点即为调度哪个进程执行、被调度进程执行的时间多久。前者称为调度策略,后者为执行时间。
CFS给cfs_rp(CFS的运行队列)中的每一个进程安排一个虚拟时钟vruntime。调度器总是选择vruntime值最低的进程执行,它记录着进程已经运行的时间,其大小与进程的权重、运行时间存在一个定量计算关系。
vruntime = 实际运行时间 * 1024 / 进程权重
所有的进程都以nice值为0的权重1024作为基准,以此来计算自己的vruntime增加速度。下面给出一些换算关系:
分配给进程的时间 = 调度周期 * 进程权重 / 全部进程权重之和
vruntime = 实际运行时间 * 1024 / 进程权重
vruntime = (调度周期 * 进程权重 / 全部进程权重之和) * 1024 / 进程权重
vruntime = (调度周期 / 全部进程权重之和) * 1024
虽然进程的权重不同,但是它们的 vruntime增长速度应该是一样的 ,与权重无关。vruntime值较小的进程,说明它以前占用cpu的时间较短,受到了不公平待遇,因此选择作为下一次运行的进程。
CFS采用当前系统中全部可调度进程优先级的比重确定每一个进程执行的时间片,即:
分配给进程的时间 = 调度周期 * 进程权重 / 全部进程之和。
1.红黑树是自平衡的,没有路径比其它任何路径长两倍以上。
2.树上运行按O(log n)时间发生(n为树中节点的数量),可以快速高效的插入或者删除任务。
盗图一波…之后继续盗图,请看红黑树数据结构
(以上内容以及相关结构均可在include/linux/sched.h中找到。)
接着我们来详细介绍一下CFS的结构。调度实体sched_entity,它代表了要给调度单位,在组调度关闭的时候可以把他等同为进程。每一个task_struct中都有一个sched_entity进程的vruntime和权重都保存在这个结构体中。
sched_entity通过红黑树组织在一起,所有的sched_entity以vruntime为key插入到红黑树中,同时缓存树的最左侧节点,也就是vruntime最小的节点,这样可以迅速选中vruntime最小的进程。
完全公平队列cfs_rq:描述运行在一个cpu上的处于TASK_RUNNING状态的普通进程的各种运行信息。
struct cfs_rq { struct load_weight load; //运行队列总的进程权重 unsigned int nr_running, h_nr_running; /进程的个数 u64 exec_clock; //运行的时钟 u64 min_vruntime; /该cpu运行队列的vruntime推进值,一般是红黑树中最小的 #ifndef CONFIG_64BIT u64 min_vruntime_copy; #endif struct rb_root tasks_timeline;//红黑树的根节点 struct rb_node *rb_leftmost;//指向vruntime值最小的节点 struct sched_entity *curr, *next, *last, *skip; #ifdef CONFIG_SCHED_DEBUG unsigned int nr_spread_over; #endif #ifdef CONFIG_SMP struct sched_avg avg; u64 runnable_load_sum; unsigned long runnable_load_avg; #ifdef CONFIG_FAIR_GROUP_SCHED unsigned long tg_load_avg_contrib; unsigned long propagate_avg; #endif atomic_long_t removed_load_avg, removed_util_avg; #ifndef CONFIG_64BIT u64 load_last_update_time_copy; #endif #ifdef CONFIG_FAIR_GROUP_SCHED unsigned long h_load; u64 last_h_load_update; struct sched_entity *h_load_next; #endif /* CONFIG_FAIR_GROUP_SCHED */ #endif /* CONFIG_SMP */ #ifdef CONFIG_FAIR_GROUP_SCHED struct rq *rq; //系统中有普通进程的运行队列,实时进程的运行队列,这些队列都包含在啊rp运行队列中 int on_list; struct list_head leaf_cfs_rq_list; struct task_group *tg; /* group that "owns" this runqueue */ #ifdef CONFIG_CFS_BANDWIDTH int runtime_enabled; u64 runtime_expires; s64 runtime_remaining; u64 throttled_clock, throttled_clock_task; u64 throttled_clock_task_time; int throttled, throttle_count; struct list_head throttled_list; #endif /* CONFIG_CFS_BANDWIDTH */ #endif /* CONFIG_FAIR_GROUP_SCHED */ };
调度实体sched_entity:记录一个进程的运行状态信息
struct sched_entity { /* For load-balancing: */ struct load_weight load; //进程的权重 struct rb_node run_node;//运行队列中的红黑树节点 struct list_head group_node;//与组调度有关 unsigned int on_rq;//进程现在是否处于TASK_RUNNING状态 u64 exec_start;//一个调度tick的开始时间 u64 sum_exec_runtime;//进程已经运行的实际时间 u64 vruntime;//虚拟运行时间 u64 prev_sum_exec_runtime;//本次调度之前,进程已经运行的时间 u64 nr_migrations; struct sched_statistics statistics; #ifdef CONFIG_FAIR_GROUP_SCHED int depth; struct sched_entity *parent;//组调度中的父进程 /* rq on which this entity is (to be) queued: */ struct cfs_rq *cfs_rq;//进程此时在哪个运行队列中 /* rq "owned" by this entity/group: */ struct cfs_rq *my_q; #endif struct sched_avg avg ____cacheline_aligned_in_smp; };
操作系统提供了一个并行执行串行进程的概念模型,进程可以动态地创建和终止,并且每个进程都有自己的地址空间。进程之间通过进程间通信原语来交换信息,一个进程可以处在运行、管理或阻塞状态。进程的调度,在Linux内核版本为2.6.24中开始引入CFS调度器,而之前是采用O(1)调度器。CFS是负责将CPU资源,分配给正在执行的进程,目标在于最大化程式互动效能,最小化整体CPU的运用。它使用红黑树来实现。
1.现代操作系统第四版(Andrew S. Tanenbaum,Herbert Bos著)
2.https://blog.csdn.net/big2chris/article/details/73322717
3.https://blog.csdn.net/XD_hebuters/article/details/79623130
4.https://www.cnblogs.com/qingjiaowoxiaoxioashou/p/5547260.html
5.https://blog.csdn.net/wangiijing/article/details/51585645
6.https://blog.csdn.net/shuizhilan/article/details/6642040
基于Linux Kernel Version 4.13.0-36-generic的源码分析进程模型
标签:动态 数据 内容 cfs 有关 9.png 关系 mic 因此
原文地址:https://www.cnblogs.com/wenwenwen/p/8973463.html