Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1575104
  • 博文数量: 399
  • 博客积分: 8508
  • 博客等级: 中将
  • 技术积分: 5302
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-14 09:28
个人简介

能力强的人善于解决问题,有智慧的人善于绕过问题。 区别很微妙,小心谨慎做后者。

文章分类

全部博文(399)

文章存档

2018年(3)

2017年(1)

2016年(1)

2015年(69)

2013年(14)

2012年(17)

2011年(12)

2010年(189)

2009年(93)

分类:

2010-04-21 20:13:11

        主要看了linux2.6.x内核进程调度那一块,和大家share一下。
        每个cpu都有一个单独的runqueue,每个runqueue中有prio_array_t *active, *expired,他们是特定时间可以互换的两个指针。前者是有timeslice剩余的array,后者是耗尽timeslice的array。prio_array的结构定义如下:
                       struct prio_array
                       {
                                   unsigned int nr_active;
                                   unsigned long bitmap[BITMAP_SIZE];
                                   struct list_head queue[MAX_PRIO];
                       };

          MAX_PRIO指的是优先级的数量,定义为139。queue是指定优先级进程list的指针,如queue[i]就是priority为 i 的进程的指针。bitmap是一张优先级的位图,或者可以说的位数组,每一位代表了一个优先级。BITMAP_SIZE宏定义如下所示:

          #define BITMAP_SIZE ((((MAX_PRIO+1+7)/8)+sizeof(long)-1)/sizeof(long)) 

         为什么要+1+7而不是直接+8,上次和TA讨论了一下,豁然开朗。+1是为了把值加到140,+7是为了向上取整。同理对于sizeof(long)- 1,他的结果是5,也就是说bitmap是32*5=160bit的位数组。这也构成了我们要用O(1)查找一个优先级最高进程的基础。这也是2.6内核相对于2.4内核的巨大进步之处。在bitmap中active的位是置1的,expired的位置0。我们使用 sched_find_first_bit()来找到第一个非0位,用 find_next_bit()来找下一个非0位。sched_find_first_bit()对每种cpu都有其特殊的执行方式,我们以i386为例,其函数如下所示:
       static inline int sched_find_first_bit(const unsigned long *b)
{
if (unlikely(b[0]))
return __ffs(b[0]);
if (unlikely(b[1]))
return __ffs(b[1]) + 32;
if (unlikely(b[2]))
return __ffs(b[2]) + 64;
if (b[3])
return __ffs(b[3]) + 96;
return __ffs(b[4]) + 128;
}
         参数b就是我们传入的bitmap,b[i]可以指的第 i 个long型非负整数,在这里我们视其为32*i~32*(i+1)的位。b[i]若为0,则说明这32位中无1,我们直接可以跳到b[i+1],若b [i]不为0,则说明其中有置位。我们调用__ffs()来找到这一位的相对位置,再加上基地址就能得出第一个置位的位置所在。我们再来看一下__ffs ()函数,我们就可以知道为什么是O(1)了:
      static inline unsigned long __ffs(unsigned long word)
{
__asm__("bsfl %1,%0"
:"=r" (word)
:"rm" (word));
return word;
}
        这是一段AT&T格式的嵌入式汇编代码,意思很简单,就是执行bsfl指令。
        bsfl汇编指令:
        intel汇编指令:bsf oprd1,oprd2;
        顺向位扫描(bit scan forward)
        从右向左(从位0-->位15或位31)扫描字或双字操作数oprd2中第一个含"1"的位,并把扫描到的第一个含'1'的位的位号送操作数oprd1。AT&T格式汇编指令bsfl类似bsf,只是源操作数和目的操作数顺序相反。正是使用了这条指令,linux把时间降到了O(1)。讲清楚了这一点,我们再回过头看,调用sched_find_first_bit()后,我们得到了优先级最高(priority最低)的active进程array的偏移idx。
        我们利用这个idx,再queue中找到queue(idx),执行之,执行过程中根据timeslice更改dynamic priority。2.6.x中又一个特殊的设计出现了,当所有进程timeslice皆耗尽时,bitmap全部被置0。此时active中没有进程,全部进程都在expired中,此时我们只要交换两个的指针就可以进行新一轮的recalculation,我们来看代码:
     array = rq->active;
if (unlikely(!array->nr_active)) {
/*Switch the active and expired arrays.*/
schedstat_inc(rq, sched_switch);
rq->active = rq->expired;
 rq->expired = array;
array = rq->active;
rq->expired_timestamp = 0;
rq->best_expired_prio = MAX_PRIO;
}
指针交换的代价是很小的:p
好了,先写到这里吧!

 

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