FreeBSD 4.5 使用分时调度算法:分配时间配额。
调度算法基于多级反馈队列。
系统会动态的调整线程的优先级, 反映出资源需求和线程消耗的资源量。系统根据线程优先级的变化在运行队列之间移动。
运行队列:系统维护三个运行队列, 分别管理实时进程, 普通进程, IDEL 进程
队列结构:
每个运行队列按优先级分为32(NQS) 个运行链, 运行链内的线程不再按优先级排序
队列的定义:
src/sys/kern/kern_switch.c
/*
* We have NQS (32) run queues per scheduling class. For the normal
* class, there are 128 priorities scaled onto these 32 queues. New
* processes are added to the last entry in each queue, and processes
* are selected for running by taking them from the head and maintaining
* a simple FIFO arrangement. Realtime and Idle priority processes have
* and explicit 0-31 priority which maps directly onto their class queue
* index. When a queue has something in it, the corresponding bit is
* set in the queuebits variable, allowing a single read to determine
* the state of all 32 queues and then a ffs() to find the first busy
* queue.
*/
struct rq queues[NQS];
struct rq rtqueues[NQS];
struct rq idqueues[NQS];
u_int32_t queuebits;
u_int32_t rtqueuebits;
u_int32_t idqueuebits;
程序类型和优先级关系:
动态调整程序优先级接口:
schedcpu()
callout, 每秒运行一次,遍历系统所有进程, 重新计算进程优先级, 更新睡眠进程的睡眠时间(p_slptime) 等。
roundrobin()
callout, 每秒运行10次(也就是说每个程序的运行时间片是10个ticks), 当CPU空闲或者当前进程不是实时进程的时候, 发送AST信号。userret()函数检查到这个信号则调mi_switch()函数抢占当前进程。从这里可以看出4BSD的进程间的抢占只发生在CPU从内核态退出到用户态的时候, 当然,sleep()函数也会发生主动的进程切换,最终也是调用mi_switch() 函数。
astpending = AST_RESCHED|AST_PENDING;
mi_switch()
执行进程切换, 最终调用由汇编实现的cpu_switch()执行与硬件相关的代码。switchticks记录了最近一次进程切换的时间, 但是似乎没有记录从idle_loop()函数发起的进程切换时间。
chooseproc()
由cpu_switch()或idle_loop()调用, 从前面提到的多级反馈队列中选择最高优先级的进程运行。