实验6:分析源代码
一、实验目的
通过阅读源代码,分析研究linux
的进程调度策略和算法。
二、实验内容
以源码为依据,回答下面的问题:
- 进程调度队列是如何组织的
- 三种调度类型(
SCHED_FIFO
,SCHED_RR
,SCHED_OHTER
)的实现过程 - 优先级是如何定义和动态变化的
- 时间片的赋值?它与优先级的关系?
- 对实时进程和多CPU的支持
- 评价
linux
的调度策略,提出改进意见
重点分析内核数据结构task_struct
(在include/linux/sched.h
中)和调度函数schedule()
(在kernel/sched.c
中)。参考有关源码分析资料,以源码为依据,写出关于上述问题的分析报告
三、实验内容
-
进程调度队列是如何组织的
所有进程被组织到以
init_task
为表头的双向链表中,通过如下宏定义#define SET_LINKS(p) do { (p)->next_task = &init_task; (p)->prev_task = init_task.prev_task; init_task.prev_task->next_task = (p); init_task.prev_task = (p); (p)->p_ysptr = NULL; if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) (p)->p_osptr->p_ysptr = p; (p)->p_pptr->p_cptr = p; } while (0)
CPU
要寻找TASK_RUNNING
状态的进程运行,在头文件已经定义#define TASK_RUNNING 0 // 相当于运行态和就绪态,此类进程都放入运行队列中
其他的进程状态定义如下
#define TASK_INTERRUPTIBLE 1 // 等待资源有效时唤醒,其他进程唤醒或定时中断唤醒,唤醒后进入就绪队列 #define TASK_UNINTERRUPTIBLE 2 // 仅资源有效时唤醒 #define TASK_ZOMBIE 4 // 进程结束但尚未消亡,未释放PCB #define TASK_STOPPED 8 // 进程暂停,等待其他进程唤醒
队列的实现来自一个双向链表结构
runqueue
。链表的每个节点是一个结构体。每次切换进程时选择优先级最高的进程上处理机运行。 -
三种调度类型(
SCHED_FIFO
,SCHED_RR
,SCHED_OHTER
)的实现过程三种调度策略的具体含义:
-
SCHED_OTHER
分时调度策略-
创建任务时指定指定优先级nice值(-20~19)。
-
将根据每个任务的
nice
值确定在cpu
上的执行时间weight
。将该任务加入到就绪队列中,weight
越小优先级越高。 -
调度程序遍历就绪队列中的任务,通过对每个任务计算优先级。
weight += 20 - p->nice;
-
计算结果最大的一个去运行,时间片用完后
weight++
,放入等待队列。if (p->mm == this_mm || !p->mm) weight += 1;
此时重复第3步
-
weight
均小于0,返回第一步
-
-
SCHED_FIFO
实时调度策略,先到先服务,除非有更高优先级任务或者主动放弃处理机,就一直运行。 -
SCHED_RR
实时调度策略,时间片轮转。时间片用完,重新分配时间片,并置于就绪队列队尾。
SHCED_RR
和SHCED_FIFO
都只用于实时任务,按照可抢占优先级调度算法进行,就绪态的实时任务立即抢占非实时任务。 -
-
优先级是如何定义和动态变化的
分时优先级与
nice
有关long nice;
实时优先级与
prio
有关unsigned long unsigned long rt_priority;
/* * This is the function that decides how desirable a process is.. * You can weigh different processes against each other depending * on what CPU they've run on lately etc to try to handle cache * and TLB miss penalties. * * Return values: * -1000: never select this * 0: out of time, recalculate counters (but it might still be * selected) * +ve: "goodness" value (the larger, the better) * +1000: realtime process, select this. */ static inline int goodness(struct task_struct *p, int this_cpu, struct mm_struct *this_mm) { int weight; /* * select the current process after every other * runnable process, but before the idle thread. * Also, dont trigger a counter recalculation. */ weight = -1; if (p->policy & SCHED_YIELD) goto out; /* * Non-RT process - normal case first. */ if (p->policy == SCHED_OTHER) { /* * Give the process a first-approximation goodness value * according to the number of clock-ticks it has left. * * Don't do any other calculations if the time slice is * over.. */ weight = p->counter; if (!weight) goto out; #ifdef CONFIG_SMP /* Give a largish advantage to the same processor... */ /* (this is equivalent to penalizing other processors) */ if (p->processor == this_cpu) weight += PROC_CHANGE_PENALTY; #endif /* .. and a slight advantage to tche current MM */ if (p->mm == this_mm || !p->mm) weight += 1; weight += 20 - p->nice; goto out; } /* * Realtime process, select the first one on the * runqueue (taking priorities within processes * into account). */ weight = 1000 + p->rt_priority; out: return weight; }
对于分时进程,给予其最合适的时间片。
对于实时进程,
nice
的值与优先级无关,但是与时间片大小有关。优先级由rt_priority
定义对于不需要切换用户空间的进程(减小了进程切换的开销),给予奖励。
-
时间片的赋值?它与优先级的关系?
定义实时进程调度权值为0~99
unsigned long rt_priority;
时间片定义如下
/* * Scheduling quanta. * * NOTE! The unix "nice" value influences how long a process * gets. The nice value ranges from -20 to +19, where a -20 * is a "high-priority" task, and a "+10" is a low-priority * task. * * We want the time-slice to be around 50ms or so, so this * calculation depends on the value of HZ. */ #if HZ < 200 #define TICK_SCALE(x) ((x) >> 2) #elif HZ < 400 #define TICK_SCALE(x) ((x) >> 1) #elif HZ < 800 #define TICK_SCALE(x) (x) #elif HZ < 1600 #define TICK_SCALE(x) ((x) << 1) #else #define TICK_SCALE(x) ((x) << 2) #endif #define NICE_TO_TICKS(nice) (TICK_SCALE(20-(nice))+1) // 换算时间配额
优先级越低(
nice
越大)时间片越小。采用FIFO策略的进程没有优先级概念。
-
对实时进程和多CPU的支持
-
关于实时进程
linux
有调度进程SCHED_RR
和SCHED_FIFO
,其优先级均高于分时进程。对于使用同一CPU
的进程,系统给予额外的奖励。
-
-
评价
linux
的调度策略,提出改进意见Linux
的调度策略偏向实时进程(或者如某些资料所说的,偏向于面对用户的交互式进程)。能够优先保证用户的交互体验。但是
linux
调度策略每次都需要遍历所有进程,在切换进程频繁的场景下,进程的切换消耗了太多资源,进而导致CPU
运行效率较低。
四、实验心得
本次实验是六次操作系统实验中工程量最大的一次,需要在一定的操作系统课程理论知识和C
语言知识的基础上,对Linux
代码进行分析,并且在读懂代码实现的基础之上,尝试理解设计的思想和目的。
在进行代码分析时,可能会遇到大量的定义,结构,跳转与分支,应当充分借助相关工具(如vim
或继承开发环境),才能更好理解代码的运行流程。