Chinaunix首页 | 论坛 | 博客
  • 博客访问: 913456
  • 博文数量: 119
  • 博客积分: 2493
  • 博客等级: 大尉
  • 技术积分: 2363
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-03 14:00
文章分类

全部博文(119)

文章存档

2013年(19)

2012年(100)

分类: LINUX

2012-08-03 19:34:56

         在2.6早期的版本使用的调度是O(1)调度,这也算是针对2.4在调度这块做了
一次大的手术,当然现在O(1)也被完全公平调度CFS调度给取代了。CFS调度也是
针对O(1)的不足而提出来的,所以分析O(1)还是很有意义的。
以下代码取自linux-2.6.15
-------------------------------------------------------------------------------------
一,可运行队列。
        内核中为每个CPU设置了一个就绪队列,该结构就是struct runqueue{};。
  1. 200 struct runqueue {
  2.  201 spinlock_t lock;
  3.  207 unsigned long nr_running;
  4.  208 #ifdef CONFIG_SMP
  5.  209 unsigned long prio_bias;
  6.  210 unsigned long cpu_load[3];
  7.  211 #endif
  8.  212 unsigned long long nr_switches;
  9.  220 unsigned long nr_uninterruptible;
  10.  221
  11.  222 unsigned long expired_timestamp;
  12.  223 unsigned long long timestamp_last_tick;
  13.  224 task_t *curr, *idle;
  14.  225 struct mm_struct *prev_mm;
  15.  226 prio_array_t *active, *expired, arrays[2];
  16.  227 int best_expired_prio;
  17.  228 atomic_t nr_iowait;
  18.  229
  19.  230 #ifdef CONFIG_SMP
  20.  231 struct sched_domain *sd;
  21.  232
  22.  233 /* For active balancing */
  23.  234 int active_balance;
  24.  235 int push_cpu;
  25.  236
  26.  237 task_t *migration_thread;
  27.  238 struct list_head migration_queue;
  28.  239 #endif
  29.  240
  30.  241 #ifdef CONFIG_SCHEDSTATS
  31.  242 /* latency stats */
  32.  243 struct sched_info rq_sched_info;
  33.  244
  34.  245 /* sys_sched_yield() stats */
  35.  246 unsigned long yld_exp_empty;
  36.  247 unsigned long yld_act_empty;
  37.  248 unsigned long yld_both_empty;
  38.  249 unsigned long yld_cnt;
  39.  250
  40.  251 /* schedule() stats */
  41.  252 unsigned long sched_switch;
  42.  253 unsigned long sched_cnt;
  43.  254 unsigned long sched_goidle;
  44.  255
  45.  256 /* try_to_wake_up() stats */
  46.  257 unsigned long ttwu_cnt;
  47.  258 unsigned long ttwu_local;
  48.  259 #endif
  49.  260 }
该结构中重点字段分析。
unsigned long nr_running:该CPU上当前就绪任务的数目,active和expired队列任务之和;
task_t * curr:该指针指向当前正在运行的进程;
prio_array_t *active:指向可运行队列中活动链表;
prio_array_t *expired:指向可运行队列中过期链表;
prio_array_t arrays[2]:为上面的两个指针开辟静态的空间;
---------------------------------------------------------------------------------------
二,优先级数组。
  1.  183 #define BITMAP_SIZE ((((MAX_PRIO+1+7)/8)+sizeof(long)-1)/sizeof(long))
  2.  184
  3.  185 typedef struct runqueue runqueue_t;
  4.  186
  5.  187 struct prio_array {
  6.  188 unsigned int nr_active;
  7.  189 unsigned long bitmap[BITMAP_SIZE];
  8.  190 struct list_head queue[MAX_PRIO];
  9.  191 };
  10.  192
该数组比较核心,MAX_PRIO的值为140,所以无论是active还是expired都是140个队列,一共
有280个队列。active表示活动进程,还有时间片。而expired表示过期进程,时间片已经运行完了
而CPU要找进程运行,会去active队列找,不会区expired队列中去找,而且找的过程的时间复杂度
是O(1),这也是为什么该调度的名字为O(1)调度。里面用了位图来描述每一个级别的队列,如果某一个
级别的队列有“队员”,也就是存在PCB,那么位图上会显示1,否则会显示0,在pick next的时候,会
从位图中计算出具有“队员”的最高优先级那一队,然后取其第一个“队员”作为调度对象。
示意图如下:

------------------------------------------------------------------------------------------
三,调度程序schedule是如何调度下一个程序的。
  1. 3049 array = rq->active;
  2. 3050 if (unlikely(!array->nr_active)) {
  3. 3051 /*
  4. 3052 * Switch the active and expired arrays.
  5. 3053 */
  6. 3054 schedstat_inc(rq, sched_switch);
  7. 3055 rq->active = rq->expired;
  8. 3056 rq->expired = array;
  9. 3057 array = rq->active;
  10. 3058 rq->expired_timestamp = 0;
  11. 3059 rq->best_expired_prio = MAX_PRIO;
  12. 3060 }
  13. 3061
  14. 3062 idx = sched_find_first_bit(array->bitmap);//获取active数组的下标索引。
  15. 3063 queue = array->queue + idx;//获取数组的表项。
  16. 3064 next = list_entry(queue->next, task_t, run_list);//获取该队列的第一个PCB
sehed_find_first_bit()用于在位图中快速查找第一个被设置的位。可以由上面给出的
图很轻松理解。

参考:
http://edsionte.com/techblog/archives/2851





阅读(2739) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~