当前位置:文档之家› 进程调度函数schedule()分析

进程调度函数schedule()分析

进程调度函数schedule()分析一.主要功能:实现进程的调度,从运行队列的链表中找到一个进程,然后进行分配。

可以由几个内核控制路径调用。

二.调用方式:1.当前进程不能获得必要资源而被阻塞,可以直接调用schedule()。

将current进程插入适当的等待队列,把状态改为TASK_INTERRUPTABLE或TASK_UNINTERRUPTABLE,然后调用schedule()。

一旦资源可用,就从等待队列删除current进程。

2.把current进程的TIF_NEED_RESCHED标志设置为1,由于会在恢复前检查这个标志的值,所以schedule()将在之后某个时间被明确调用,以延迟方式调用调度程序。

三. 功能分析:asmlinkage void schedule(void){struct schedule_data * sched_data;struct task_struct *prev, *next, *p;struct list_head *tmp;int this_cpu, c; //定义变量,*prev为调度之前进程,*next为调度之后进程spin_lock_prefetch(&runqueue_lock);BUG_ON(!current->active_mm);//current进程的active_mm为空,出错need_reshced_back;prev=current;//变量初始化this_cpu = prev->processor;(unlikely(in_interrupt())) {printk("Scheduling in interrupt\n");BUG();}//不能再中断程序中执行schedule(),否则会出错release_kernel_lock(prev, this_cpu);sched_data = & aligned_data[this_cpu].schedule_data;spin_lock_irq(&runqueue_lock);//释放内核锁,开this_cpu中断,并锁住runqueue队列/* move an exhausted RR process to be last.. */if (unlikely(prev->policy == SCHED_RR))//是否为轮转调度if (!prev->counter) {prev->counter = NICE_TO_TICKS(prev->nice);move_last_runqueue(prev);}//prev->counter为逐渐减少的进程时间配额,当为0时就从当前位置到runqueue 的末尾。

NICE_TO_TICKS将优先级别换算为时间配额,即恢复开始的时间配额switch (prev->state) {case TASK_INTERRUPTIBLE:if (signal_pending(prev)) {prev->state = TASK_RUNNING;break;}//查看当前状态,如果处于TASK_INTERRUPTIBLE且有信号等待处理,内核将状态设置为TASK_RUNNING,让其处理完default:del_from_runqueue(prev);case TASK_RUNNING:;}prev->need_resched = 0;//如果没有信号等待,从等待队列撤出;如果处在TASK_RUNNING 继续进行/** this is the scheduler proper:*/repeat_schedule:/** Default process to select..*/next = idle_task(this_cpu);//指向最佳候选c = -1000;//进程的综合权值list_for_each(tmp, &runqueue_head) {p = list_entry(tmp, struct task_struct, run_list);if (can_schedule(p, this_cpu)) {int weight = goodness(p, this_cpu, prev->active_mm);if (weight > c)//比较权值c = weight, next = p;}}//调度之前将要调度的进程默认为0号,权值为-1000。

0号进程即不会睡眠,也不会死亡。

内核遍历可执行队列runqueue的每个进程,用godness计算权值,然后进行比较/* Do we need to re-calculate counters? */if (unlikely(!c)) {struct task_struct *p;spin_unlock_irq(&runqueue_lock);//锁住runqueueread_lock(&tasklist_lock);//锁住进程的tasklistfor_each_task(p)//对每个进程执行操作p->counter = (p->counter >> 1) + NICE_TO_TICKS(p->nice);read_unlock(&tasklist_lock);spin_lock_irq(&runqueue_lock);goto repeat_schedule;}//若c为0,说明运行队列中的所有权值都为0,要重新分配/** from this point on nothing can prevent us from* switching to the next task, save this fact in* sched_data.*/sched_data->curr = next;task_set_cpu(next, this_cpu);spin_unlock_irq(&runqueue_lock);//对队列解锁,开中断if (unlikely(prev == next)) {/* We won't go through the normal tail, so do this by hand */prev->policy &= ~SCHED_YIELD;goto same_process;} //如果选中是原来进程,则另外执行操作#ifdef CONFIG_SMP/** maintain the per-process 'last schedule' value.* (this has to be recalculated even if we reschedule to* the same process) Currently this is only used on SMP,* and it's approximate, so we do not have to maintain* it while holding the runqueue spinlock.*/sched_data->last_schedule = get_cycles();/** We drop the scheduler lock early (it's a global spinlock),* thus we have to lock the previous process from getting* rescheduled during switch_to().*/#endif /* CONFIG_SMP */kstat.context_swtch++;/** there are 3 processes which are affected by a context switch:** prev == .... ==> (last => next)** It's the 'much more previous' 'prev' that is on next's stack,* but prev is set to (the just run) 'last' process by switch_to().* This might sound slightly confusing but makes tons of sense.*/prepare_to_switch();{struct mm_struct *mm = next->mm;struct mm_struct *oldmm = prev->active_mm;if (!mm) {BUG_ON(next->active_mm);next->active_mm = oldmm;atomic_inc(&oldmm->mm_count);enter_lazy_tlb(oldmm, next, this_cpu);} else {BUG_ON(next->active_mm != mm);switch_mm(oldmm, mm, next, this_cpu);}//开始进程切换,如果是内核线程,借用prev的地址空间,如果是一般进程,切换到next的用户空间if (!prev->mm) {prev->active_mm = NULL;mmdrop(oldmm);}}//如果是内核线程,则释放地址空间,将mm_struct的共享计数减一。

/** This just switches the register state and the* stack.*/switch_to(prev, next, prev);__schedule_tail(prev);same_process:reacquire_kernel_lock(current);if (current->need_resched)goto need_resched_back;return;}//针对smp(smp是多处理机系统),如果调度标志位不为0,重新调度四.分析结论与体会:通过分析,了解到shedule()的运行过程,一些linux常见的变量以及库函数。

它的最主要作用就是从就绪进程中选择一个优先级最高的进程来代替当前进程运行。

由于内核的重要性,掌握理解其功能是编写任何设备驱动的必要条件。

相关主题