操作系统实验六

实验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)的实现过程

    三种调度策略的具体含义:

    1. 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,返回第一步

    2. SCHED_FIFO实时调度策略,先到先服务,除非有更高优先级任务或者主动放弃处理机,就一直运行。

    3. SCHED_RR实时调度策略,时间片轮转。时间片用完,重新分配时间片,并置于就绪队列队尾。

    SHCED_RRSHCED_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的支持

    1. 关于实时进程

      linux有调度进程SCHED_RRSCHED_FIFO,其优先级均高于分时进程。对于使用同一CPU的进程,系统给予额外的奖励。

  • 评价linux的调度策略,提出改进意见

    Linux的调度策略偏向实时进程(或者如某些资料所说的,偏向于面对用户的交互式进程)。能够优先保证用户的交互体验。

    但是linux调度策略每次都需要遍历所有进程,在切换进程频繁的场景下,进程的切换消耗了太多资源,进而导致CPU运行效率较低。

四、实验心得

本次实验是六次操作系统实验中工程量最大的一次,需要在一定的操作系统课程理论知识和C语言知识的基础上,对Linux代码进行分析,并且在读懂代码实现的基础之上,尝试理解设计的思想和目的。

在进行代码分析时,可能会遇到大量的定义,结构,跳转与分支,应当充分借助相关工具(如vim或继承开发环境),才能更好理解代码的运行流程。

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值