进程调度需要兼顾3种进程:交互进程,批处理进程,实时进程,在设计一个进程调度机制时需要考虑具体问题
(1)调度时机?
答:进程在用户空间可以pause()或者让内核设置进程为睡眠状态,以此调度,调度还可以强制性的发生在从系统调用返回前夕,以此每次从中断或异常处理返回到用户空间前夕(用户空间表示,只有cpu在用户空间运行时,发生异常或者中断才会调度),如果发生在内核的异常或者中断不会引起调度

 
缺陷:在实时进程中,内核中发生了中断,而且这个中断处理时间很长,并且内核中断不会调度.那就可能将调度过分延迟,从而使得用户感觉到明显的延迟.,另外从内核返回到用户空间并非一定会调度,而取决于pcb中的need_resched是否设置为1(谁来设置呢,当前进程自动让粗,在内核唤醒一个进程,以及时间中断处理程序发现当前进程运行时间太久时)
(2)调度的政策,依靠什么标准调度下一进程
答:用户抢占,时机从内核态返回到用户态,内核不可抢占(2.6内核版本改进).
内核为每个进程计算一个权值,选最高运行,进程运行时,资格随时间低调,当所有进程的资格变为0时,就从新计算一次(2.6内核改进,每个,而非全部)
为了适应不同策略,分为:sched_fifo(实时进程)跟sched_rr(运行时间长的进程)还有other
(3)调度的方式:可抢占式,还是不可抢占式
  1. /*
  2. * 'schedule()' is the scheduler function. It's a very simple and nice
  3. * scheduler: it's not perfect, but certainly works for most things.
  4. *
  5. * The goto is "interesting".
  6. *
  7. * NOTE!! Task 0 is the 'idle' task, which gets called when no other
  8. * tasks can run. It can not be killed, and it cannot sleep. The 'state'
  9. * information in task[0] is never used.
  10. */
  11. asmlinkage void schedule(void)
  12. {
  13. struct schedule_data * sched_data;
  14. struct task_struct *prev, *next, *p;
  15. struct list_head *tmp;
  16. int this_cpu, c;
  17. if (!current->active_mm) BUG();//调度时,线程的active_mm不可以为0,借用之前的空间
  18. need_resched_back:
  19. prev = current;//赋值获得当前pcb
  20. this_cpu = prev->processor;
  21. if (in_interrupt())//是否处于中断处理状态,一个bug,将调用bug()
  22. goto scheduling_in_interrupt;
  23. release_kernel_lock(prev, this_cpu);//对单核cpu是空语句
  24. /*检查内核软中断服务请求是否在等待 Do "administrative" work here while we don't hold any locks */
  25. if (softirq_active(this_cpu) & softirq_mask(this_cpu))
  26. goto handle_softirq;//转到下面,进行请求服务
  27. handle_softirq_back:
  28. /*sched_data用于保存一下一次调度时,所需要的信息
  29. * 'sched_data' is protected by the fact that we can run
  30. * only one process per CPU.
  31. */
  32. sched_data = & aligned_data[this_cpu].schedule_data;
  33. spin_lock_irq(&runqueue_lock);//加锁此队列
  34. /* move an exhausted RR process to be last.. */
  35. if (prev->policy == SCHED_RR)//如果当前进程的调度策略为sched_rr也就是轮换调度,那就特殊处理
  36. goto move_rr_last;//判断时间配额是否用完,用完移到run队列队尾,同时恢复最初时间配额,然后跳到这里
  37. move_rr_back://对sched_rr特殊处理
  38. switch (prev->state) {
  39. case TASK_INTERRUPTIBLE:
  40. if (signal_pending(prev)) {//检测当前进程是否有信号要进行处理
  41. prev->state = TASK_RUNNING;
  42. break;
  43. }
  44. default:
  45. del_from_runqueue(prev);//从可运行队列中删除
  46. case TASK_RUNNING:
  47. }
  48. prev->need_resched = ;//设置为不需要调度,因为所需求的调度已经在运行了
  49. /*
  50. * this is the scheduler proper:
  51. */
  52. repeat_schedule://接下来挑选一进程来运行了
  53. /*
  54. * Default process to select..
  55. */
  56. next = idle_task(this_cpu);//指向最佳候选进程
  57. c = -;//设置c的权值为最低值,后面遍历有用
  58. if (prev->state == TASK_RUNNING)//如果当前进程还是处于可运行状态
  59. goto still_running;//如果当前进程还想继续运行,那就从当前进程计算权值开始,相同权值具有优先级
  60. still_running_back:
  61. list_for_each(tmp, &runqueue_head) {
  62. p = list_entry(tmp, struct task_struct, run_list);
  63. if (can_schedule(p, this_cpu)) {//遍历运行队列中的所有进程
  64. int weight = goodness(p, this_cpu, prev->active_mm);//通过goodness计算机它当前所具有的权值
  65. if (weight > c)
  66. c = weight, next = p;
  67. }
  68. }
  69. /* Do we need to re-calculate counters? */
  70. if (!c)//如果已选择的进程(权值最高)为0,那就要从新计算机各个进程的时间配额,说明系统已经没有就绪的实时进程了
  71. goto recalculate;
  72. /*
  73. * from this point on nothing can prevent us from
  74. * switching to the next task, save this fact in
  75. * sched_data.
  76. */
  77. sched_data->curr = next;
  78. #ifdef CONFIG_SMP
  79. next->has_cpu = ;
  80. next->processor = this_cpu;
  81. #endif
  82. spin_unlock_irq(&runqueue_lock);
  83. if (prev == next)//如果挑选出来的进程是当前进程,那就直接返回
  84. goto same_process;
  85. #ifdef CONFIG_SMP
  86. /*
  87. * maintain the per-process 'last schedule' value.
  88. * (this has to be recalculated even if we reschedule to
  89. * the same process) Currently this is only used on SMP,
  90. * and it's approximate, so we do not have to maintain
  91. * it while holding the runqueue spinlock.
  92. */
  93. sched_data->last_schedule = get_cycles();
  94. /*
  95. * We drop the scheduler lock early (it's a global spinlock),
  96. * thus we have to lock the previous process from getting
  97. * rescheduled during switch_to().
  98. */
  99. #endif /* CONFIG_SMP */
  100. kstat.context_swtch++;
  101. /*
  102. * there are 3 processes which are affected by a context switch:
  103. *
  104. * prev == .... ==> (last => next)
  105. *
  106. * It's the 'much more previous' 'prev' that is on next's stack,
  107. * but prev is set to (the just run) 'last' process by switch_to().
  108. * This might sound slightly confusing but makes tons of sense.
  109. */
  110. prepare_to_switch();//准备调度
  111. {
  112. struct mm_struct *mm = next->mm;//下一进程的mm
  113. struct mm_struct *oldmm = prev->active_mm;//当前进程的mm
  114. if (!mm) {//下一要调度的是线程
  115. if (next->active_mm) BUG();//如果线程连空间都木有,那就bug
  116. next->active_mm = oldmm;//沿用前一进程的空间
  117. atomic_inc(&oldmm->mm_count);//引用计数++
  118. enter_lazy_tlb(oldmm, next, this_cpu);
  119. } else {//下一要调度的是进程
  120. if (next->active_mm != mm) BUG();
  121. switch_mm(oldmm, mm, next, this_cpu);//切换空间
  122. }
  123. if (!prev->mm) {//前一进程为线程
  124. prev->active_mm = NULL;//设置为NULL
  125. mmdrop(oldmm);//释放,这里线程只是把引用计数--
  126. }
  127. }
  128. /*
  129. * This just switches the register state and the
  130. * stack.
  131. */
  132. switch_to(prev, next, prev);//开始调度------------------
  133. __schedule_tail(prev);//对于新创建的进程,调用后,直接转到ret_from_sys_call返回到用户空间
  134. same_process:
  135. reacquire_kernel_lock(current);//空语句
  136. if (current->need_resched)//前面已经清空为0,现在变成了非0,那就中断发生了有变化
  137. goto need_resched_back;//再次调度
  138. return;
  139. recalculate:
  140. {
  141. struct task_struct *p;
  142. spin_unlock_irq(&runqueue_lock);
  143. read_lock(&tasklist_lock);
  144. for_each_task(p)//将当前进程的时间配额除以2?nice换来的ticks数量
  145. p->counter = (p->counter >> ) + NICE_TO_TICKS(p->nice);
  146. read_unlock(&tasklist_lock);
  147. spin_lock_irq(&runqueue_lock);
  148. }
  149. goto repeat_schedule;
  150. still_running:
  151. c = goodness(prev, this_cpu, prev->active_mm);
  152. next = prev;
  153. goto still_running_back;
  154. handle_softirq:
  155. do_softirq();
  156. goto handle_softirq_back;
  157. move_rr_last:
  158. if (!prev->counter) {//一旦counter为0,表示运行时间配额为0,将从可执行进程队列当前位置移到队列尾部
  159. prev->counter = NICE_TO_TICKS(prev->nice);//恢复最初的时间配额.将根据进程的优先级别换成可运行的时间配额.
  160. move_last_runqueue(prev);
  161. }
  162. goto move_rr_back;
  163. scheduling_in_interrupt://一个bug,在中断处理程序中调度了
  164. printk("Scheduling in interrupt\n");
  165. BUG();
  166. return;
  167. }

goodness函数解析

goodness对于非实时进程来说权重等于时间配额+1(如果是线程,+1)+(20-nice)

nice对于实时进程的权重计算没什么用,不过对sched_rr的时间配额有用

实时进程权重计算:weight = 1000 + p->rt_priority,rt_priority对实时进程的权重还是很重要的
  1. static inline int goodness(struct task_struct * p, int this_cpu, struct mm_struct *this_mm)
  2. {
  3. int weight;
  4. /*
  5. * select the current process after every other
  6. * runnable process, but before the idle thread.
  7. * Also, dont trigger a counter recalculation.
  8. */
  9. weight = -;
  10. if (p->policy & SCHED_YIELD)//如果当前进程设置了此标志位,表示礼让,权值设置为-1.直接return
  11. goto out;
  12. /*
  13. * Non-RT process - normal case first.
  14. */
  15. if (p->policy == SCHED_OTHER) {//对于没有实时要求的进程来说
  16. /*
  17. * Give the process a first-approximation goodness value
  18. * according to the number of clock-ticks it has left.
  19. *
  20. * Don't do any other calculations if the time slice is
  21. * over..
  22. */
  23. weight = p->counter;//weight等于时间配额
  24. if (!weight)//用完了,权值为0,直接返回
  25. goto out;
  26.  
  27. #ifdef CONFIG_SMP
  28. /* Give a largish advantage to the same processor... */
  29. /* (this is equivalent to penalizing other processors) */
  30. if (p->processor == this_cpu)
  31. weight += PROC_CHANGE_PENALTY;
  32. #endif
  33. /* .. and a slight advantage to the current MM */
  34. if (p->mm == this_mm || !p->mm)//如果是内核线程,或者用户空间与当前进程相同,唔需要切换用户空间,获得奖励+1s
  35. weight += ;
  36. weight += - p->nice;//nice也小,优先级越高,范围-20到19.
  37. goto out;
  38. }
  39. /*
  40. * Realtime process, select the first one on the
  41. * runqueue (taking priorities within processes
  42. * into account).//实时进程的nice与优先级无关,但对于sched_rr进程的时间配额大小有关,实时进程就绪时,非实时进程没机会运行
  43. *///对于实时进程来说,则有一种正向优先级,那就是实时优先级rt_priority,由于时间要求,对进程赋予很高的全职
  44. weight = + p->rt_priority;//rt_priotty对实时进程哟很重要的作用
  45. out:
  46. return weight;
  47. }
总schedule流程:
准备:
处理中断处理状态直接跳到bug()出错
当前进程是SCHED_RR(需要长时间的进程),判断时间片是否用完了,用完了移到run队尾,同时恢复时间片配额
判断当前进程是否是可中断睡眠,是而且有信号要处理,那就设置当前进程为可运行状态;,如果是除了运行状态的其他状态
那就把当前进程从可运行状态队列删除.
挑选:
如果当前进程处于run.计算权重从当前进程计算,这样使得当前进程在同权重的进程中有优先级
遍历所有运行队列中的所有进程,通过goodness(goodness对于非实时进程来说权重=时间配额+1(如果是线程,+1)+(20-nice)
nice对于实时进程的权重计算没什么用,不过对sched_rr的时间片配额有用,实时进程权重计算:weight = 1000 + p->rt_priority)
计算所有run状态的权重,选取最高的运行,不过如果最高的是0,那就表示运行队列中没有实时进程,
需要重新计算可运行状态队列中的所有进程的时间片.而且这种情况持续一段时间了,否则sched_other没机会消耗到0
,计算完后.选最高权重进程调度
调度:
切换空间,切换进程或线程
 

linux2.4内核调度的更多相关文章

  1. Linux2.6内核实现的是NPTL

    NPTL是一个1×1的线程模型,即一个线程对于一个操作系统的调度进程,优点是非常简单.而其他一些操作系统比如Solaris则是MxN的,M对应创建的线程数,N对应操作系统可以运行的实体.(N<M ...

  2. Linux2.6 内核的 Initrd 机制解析

    文章来自:www.ibm.com/developerworks/cn/linux/l-k26initrd/ 1.什么是 Initrd initrd 的英文含义是 boot loader initial ...

  3. Linux2.6 内核的 Initrd 机制解析(转)

    from: https://www.ibm.com/developerworks/cn/linux/l-k26initrd/ 简介: Linux 的 initrd 技术是一个非常普遍使用的机制,lin ...

  4. Linux 虚存 linux2.6内核特性

    一.大型页面的支持 当代计算机体系结构大都支持多种页面大小,例如,IA-32体系结构支持4KB或4MB的页面, Linux操作系统只是将大型页面用于映射实际的内核映像.大型页面的使用主要是为了改进高性 ...

  5. Linux 内核调度器源码分析 - 初始化

    导语 上篇系列文 混部之殇-论云原生资源隔离技术之CPU隔离(一) 介绍了云原生混部场景中CPU资源隔离核心技术:内核调度器,本系列文章<Linux内核调度器源码分析>将从源码的角度剖析内 ...

  6. 鸿蒙内核源码分析(调度故事篇) | 用故事说内核调度 | 百篇博客分析OpenHarmony源码 | v9.07

    百篇博客系列篇.本篇为: v09.xx 鸿蒙内核源码分析(调度故事篇) | 用故事说内核调度过程 | 51.c.h .o 前因后果相关篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...

  7. 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 百篇博客分析OpenHarmony源码 | v4.05

    百篇博客系列篇.本篇为: v04.xx 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  8. Linux2.6内核进程调度系列--scheduler_tick()函数1.总体思想

    参考的是ULK第三版,Linux2.6.11.12内核版本. 调度程序依靠几个函数来完成调度工作,其中最重要的第一个函数是scheduler_tick函数,主要步骤如下: /** * 维持当前最新的t ...

  9. Linux2.6内核--进程调度理论

    从1991年Linux的第1版到后来的2.4内核系列,Linux的调度程序都相当简陋,设计近乎原始,见0.11版内核进程调度.当然它很容易理解,但是它在众多可运行进程或者多处理器的环境下都难以胜任. ...

随机推荐

  1. 揭密 Vue 的双向绑定

    Vue 中需要输入什么内容的时候,自然会想到使用 <input v-model="xxx" /> 的方式来实现双向绑定.下面是一个最简单的示例 剖析Vue原理& ...

  2. vue.js 服务端渲染nuxt.js反向代理nginx部署

    vue.js的官方介绍里可能提到过nuxt.js,我也不太清楚我怎么找到这个的 最近项目vue.js是主流了,当有些优化需求过来后,vue还是有点力不从心, 比如SEO的优化,由于vue在初始化完成之 ...

  3. UVA:11297-Census(二维线段树)

    Census Time Limit: 8 sec Description This year, there have been many problems with population calcul ...

  4. Uva:11401-Triangle Counting

    Triangle Counting Time limit1000 ms Description You are given n rods of length 1, 2-, n. You have to ...

  5. Gym 100829S_surf 动态规划的优化

    题目大意是,非你若干个任务,任务分别对应开始时间.预期收益.持续时间三项指标,让你从中选择一个受益最大的方案(没有开始时间相同的任务). 于是,标准状态转移方程应当为,设DP[K]为选择了前K个任务的 ...

  6. ListNode Java创建链表

    用了一种自创的比较简洁的方式来创建链表 class ListNode { //为了方便,这两个变量都使用pub1ic, //存放数据的变量,直接为int型 public int data; //存放结 ...

  7. hadoop ha集群搭建

    集群配置: jdk1.8.0_161 hadoop-2.6.1 zookeeper-3.4.8 linux系统环境:Centos6.5 3台主机:master.slave01.slave02 Hado ...

  8. SpringMvc路径参数和url的两种实现方式

    我们经常采用的SpringMvc路径参数经常的操作是在url后面采用?参数名=值1&参数名2=值2这种方式实现 RequestMapping的作用: 1)当作用在controller时,我们通 ...

  9. 极简配置phpstorm+xdebug进行断点调试

    以前调试的时候各种var_dump()就能得到结果,现在入手别人开发的工作,由于不了解业务和代码逻辑,又要去修改bug,就造成了修改bug效率低,所以又拾起来了xdbug,顺便总结了一下phpstor ...

  10. 聊聊、Integer 封装特性

    前几天在公司内部群,有人分享出了一道题,问谁能口算出来,他就膜拜那个人.题目如下: Class cache = Integer.class.getDeclaredClasses()[0]: Field ...