Lab6:进程的调度
CPU调度
从就绪队列中挑选下一个占用CPU运行的进程,从多个可用CPU中挑选就绪进程可使用的CPU资源
调度策略
比较调度算法的准则
- CPU使用率
- 吞吐量
- 周转时间
- 就绪等待时间
- 响应时间
吞吐量与延迟
低延迟:喝水的时候想要一打开水龙头水就流出来
高带宽:给游泳池充水时希望从水龙头里同时流出大量的水,并且不介意是否存在延迟
处理机调度策略的响应时间目标
- 减少响应时间
- 减少平均响应时间的波动
- 增加吞吐量
- 减少等待时间
调度算法
先来先服务算法(First Come First Served, FCFS)
依据进程进入就绪状态的先后顺序排列,进程进入等待或结束状态时,就绪队列中的下一个进程占用CPU
但是FCFS的平均等待时间波动较大,I/O资源和CPU资源的利用率较低
短进程优先算法(SPN)
选择就绪队列中执行时间最短进程占用CPU进入运行状态,用历史的执行时间来预估未来的执行时间,短进程优先算法具有最优平均周转时间
但是连续的短进程流会使长进程无法获得CPU资源
最高响应比优先算法(HRRN)
选择就绪队列中响应比R值最高的进程
R=(w+s)/s
w: 等待时间(waiting time)
s: 执行时间(service time)
时间片轮转算法(RR, Round-Robin)
利用时间片作为分配处理机资源的基本时间单元,时间片结束时,按FCFS算法切换到下一个就绪进程, 每隔(n – 1)个时间片进程执行一个时间片q
但是时间片轮转算法需要选择好时间片的大小,过大过小都会导致效率问题
多级队列调度算法(MQ)
就绪队列被划分成多个独立的子队列,如:前台–RR、后台–FCFS
多级反馈队列算法(MLFQ)
进程可在不同队列间移动的多级队列算法,时间片大小随优先级级别增加而增加,如进程在当前的时间片没有完成,则降到下一个优先级
公平共享调度(FSS, Fair Share Scheduling)
FSS控制用户对系统资源的访问,一些用户组比其他用户组更重要,保证不重要的组无法垄断资源,未使用的资源按比例分配,没有达到资源使用率目标的组获得更高的优先级
代码实现
这个实验其实有两个,一个是实现Round Robin,一个是Stride Scheduling,两个都非常简单。
Round Robin调度算法的调度思想是让所有 runnable 态的进程分时轮流使用 CPU 时间。Round Robin 调度器维护当前 runnable进程的有序运行队列。当前进程的时间片用完之后,调度器将当前进程放置到运行队列的尾部,再从其头部取出进程进行调度。
Stride Scheduling
具体看一下Stride Scheduling
1、为每个runnable的进程设置一个当前状态stride,表示该进程当前的调度权。另外定义其对应的pass值,表示对应进程在调度后,stride 需要进行的累加值。
2、每次需要调度时,从当前 runnable 态的进程中选择 stride最小的进程调度。对于获得调度的进程P,将对应的stride加上其对应的步长pass(只与进程的优先权有关系)。
3、在一段固定的时间之后,回到步骤2,重新调度当前stride最小的进程
static void
stride_init(struct run_queue *rq) {
/* LAB6: YOUR CODE */
list_init(&(rq->run_list));
rq->lab6_run_pool = NULL;
rq->proc_num = 0;
}
首先是队列初始化函数
static void
stride_enqueue(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
rq->lab6_run_pool = skew_heap_insert(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f);
#else
assert(list_empty(&(proc->run_link)));
list_add_before(&(rq->run_list), &(proc->run_link));
#endif
if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) {
proc->time_slice = rq->max_time_slice;
}
proc->rq = rq;
rq->proc_num ++;
}
然后是入队函数stride_enqueue,根据之前对该调度算法的分析,这里函数主要是初始化刚进入运行队列的进程 proc 的stride属性,然后比较队头元素与当前进程的步数大小,选择步数最小的运行,即将其插入放入运行队列中去,这里并未放置在队列头部。最后初始化时间片,然后将运行队列进程数目加一。
static void
stride_enqueue(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
rq->lab6_run_pool = skew_heap_insert(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f);
#else
assert(list_empty(&(proc->run_link)));
list_add_before(&(rq->run_list), &(proc->run_link));
#endif
if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) {
proc->time_slice = rq->max_time_slice;
}
proc->rq = rq;
rq->proc_num ++;
}
static void
stride_dequeue(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
rq->lab6_run_pool = skew_heap_remove(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f); //从优先队列中移除
#else
assert(!list_empty(&(proc->run_link)) && proc->rq == rq);
list_del_init(&(proc->run_link));
#endif
rq->proc_num --;
}
static struct proc_struct *
stride_pick_next(struct run_queue *rq) {
/* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
if (rq->lab6_run_pool == NULL) return NULL;
struct proc_struct *p = le2proc(rq->lab6_run_pool, lab6_run_pool);
#else
list_entry_t *le = list_next(&(rq->run_list));
if (le == &rq->run_list)
return NULL;
struct proc_struct *p = le2proc(le, run_link);
le = list_next(le);
while (le != &rq->run_list)
{
struct proc_struct *q = le2proc(le, run_link);
if ((int32_t)(p->lab6_stride - q->lab6_stride) > 0)
p = q;
le = list_next(le);
}
#endif
//更新对应进程的stride值
if (p->lab6_priority == 0)
p->lab6_stride += BIG_STRIDE;
else p->lab6_stride += BIG_STRIDE / p->lab6_priority;
return p;
}
接下来就是进程的调度函数stride_pick_next,观察代码,它的核心是先扫描整个运行队列,返回其中stride值最小的对应进程,然后更新对应进程的stride值,将步长设置为优先级的倒数,如果为0则设置为最大的步长。
static void
stride_proc_tick(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
if (proc->time_slice > 0)
{
proc->time_slice --;
}
if (proc->time_slice == 0)
{
proc->need_resched = 1;
}
}
最后是时间片函数stride_proc_tick,主要工作是检测当前进程是否已用完分配的时间片。
相对于这两个算法我觉得更重要的是明白进程的调度时机
- 时钟中断处理函数检测到时间片到期了
- 发生阻塞或者睡眠等情况
最新文章
- IE10(去掉文本框的X)
- js 的一些知识 摘自http://img0.pconline.com.cn/Pc_intranet/1105/13/313647_7.pdf
- UVa 11774 (置换 找规律) Doom's Day
- [转]IOS, xib和storyboard的混用
- error when loading the sdk 发现了元素 d:skin 开头无效内容
- JQuery弹出层,点击按钮后弹出遮罩层,有关闭按钮
- Sizzle一步步实现所有功能(层级选择)
- Jacobi symbol(裸雅可比符号)
- mongodb 数据备份与恢复
- 8 个最好的 jQuery 树形 Tree 插件
- JDK源码分析(12)之 ConcurrentHashMap 详解
- web api 安全
- bootstrap实现列的拖动
- Confluence 6 用户目录图例 - 连接 Jira
- POJ3635 Full Tank?【Dijkstra+DP】
- Dom4j用Xpath获取节点——(六)
- SharePoint 2010 使用Install-SPSolution部署wsp包状态一直是”正在部署”
- 第三届CCF软件能力认证
- Kafka单机环境的部署
- python 判断一个对象的变量类型
热门文章
- Git的安装及布置
- SpringCloud之Nacos服务发现(十七)
- Okhttp 请求流程梳理
- ArcGIS Engine连接ArcSDE SQL Server(获得所有SDE图层)
- [2018-01-12] laravel--ORM
- (七)golang-变量之基本数据类型(看这篇就够了)
- JS中获取元素属性的逆天大法
- 爬虫学习--Urllib库基本使用 Day1
- Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?
- 易初大数据——2019年10月17日 王庆超 spss