主要看了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
好了,先写到这里吧!
每个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
好了,先写到这里吧!