• [进程管理] linux核心调度器


            linux核心调度器主要基于两个函数实现:周期性调度器函数和主调度器函数。这些函数会根据现有进程的优先级分配CPU时间,所以也称“优先调度”

             一、周期性调度器

            周期性调度器是在函数scheduler_tick(void),如果当前系统正在活动中,那么内核会按照CPU频率HZ自动调用该函数。如果运行队列中没有进程,则等待;如果电力不足,也会关闭该调度器以减少电能消耗。(基于内核版本3.1.6)

            该函数有下面两个主要任务。
                    (1) 管理内核中与整个系统和各个进程的调度相关的统计量。
                    (2) 激活负责当前进程的调度类的周期性调度方法。

    void scheduler_tick(void)
    {
    	int cpu = smp_processor_id(); /* 获取CPU Id号*/
    	struct rq *rq = cpu_rq(cpu);   /* 获取对应的运行队列 */
    	struct task_struct *curr = rq->curr; /* 获取当前线程描述符 */
    
    	sched_clock_tick();
    
    	raw_spin_lock(&rq->lock);  /* 加锁 */
    	update_rq_clock(rq);    /* 处理就绪队列始终的更新: 本质是增加struct rq 当前实例的时钟时间戳 */
    	update_cpu_load_active(rq); /* 更新就绪队列的cpu_load[]数据: 本质是讲数组中先前存储的负荷值向后移动一个位置,将当前负荷记入数组的第一个位置 */
    	curr->sched_class->task_tick(rq, curr, 0); /* 激活当前线程的调度类的周期性调度方法,不同的调度方法有不同的实现 */
    	raw_spin_unlock(&rq->lock);/* 解锁 */
    
    	perf_event_task_tick(); /* 与perf计数事件相关 */
    
    #ifdef CONFIG_SMP
    	rq->idle_at_tick = idle_cpu(cpu); /* 当前CPU是否空闲 */
    	trigger_load_balance(rq, cpu); /* 如果到是时候进行周期性负载平衡则触发SCHED_SOFTIRQ */
    #endif
    }

            二、主调度器

            周期性调度器是在函数scheduler(void)中实现。

            1. 主要功能:从运行的队列中选择一个合适的线程,并进行线程切换;

            2. 代码分析:

    asmlinkage void __sched schedule(void)
    {
    	struct task_struct *tsk = current; /* 获取当前线程的描述符 */
    
    	sched_submit_work(tsk); /* 为了防止死锁 */
    	__schedule(); /* 真正的主调度去实现方法 */
    }


    static void __sched __schedule(void)
    {
    	struct task_struct *prev, *next;
    	unsigned long *switch_count;
    	struct rq *rq;
    	int cpu;
    
    need_resched:
    	preempt_disable();/* 禁止抢占 */
    	cpu = smp_processor_id(); /* 获取当前CPU ID 号 */
    	rq = cpu_rq(cpu); /* 获取当前CPU 对应的运行队列 */
    	rcu_note_context_switch(cpu); /* 与RCU相关 */
    	prev = rq->curr;
    
    	schedule_debug(prev); /* 记录debug信息 */
    
    	if (sched_feat(HRTICK))
    		hrtick_clear(rq);
    
    	raw_spin_lock_irq(&rq->lock);
    
    	switch_count = &prev->nivcsw;
    	if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
    		if (unlikely(signal_pending_state(prev->state, prev))) {
    			prev->state = TASK_RUNNING;
    		} else {
    			deactivate_task(rq, prev, DEQUEUE_SLEEP); /* 使进程停止活动,并最终调用具体的dequeue_task()方法 */
    			prev->on_rq = 0;
    
    			/*
    			 * If a worker went to sleep, notify and ask workqueue
    			 * whether it wants to wake up a task to maintain
    			 * concurrency.
    			 */
    			if (prev->flags & PF_WQ_WORKER) { /* 一些并发性判断和操作,不影响整体调度流程 */
    				struct task_struct *to_wakeup;
    
    				to_wakeup = wq_worker_sleeping(prev, cpu);
    				if (to_wakeup)
    					try_to_wake_up_local(to_wakeup);
    			}
    		}
    		switch_count = &prev->nvcsw;
    	}
    
    	pre_schedule(rq, prev);
    
    	if (unlikely(!rq->nr_running))
    		idle_balance(cpu, rq);/* 当前运行队列为空,调用idle_balance()试图从其他的CPU运行队列拉来一些进程 */
    
    	put_prev_task(rq, prev); /* 通知调度器类当前运行的进程将要被另一个进程代替,为某些计数器更新计数提供实际 */
    	next = pick_next_task(rq);/* 从运行队列中选择下一个运行线程 */
    	clear_tsk_need_resched(prev);/* 清除当前进程描述符中的重调度标志TIF_NEED_RESCHED */
    	rq->skip_clock_update = 0;
    
    	if (likely(prev != next)) { /* 判断选择的next线程是不是原来的线程 */
    		rq->nr_switches++;
    		rq->curr = next;
    		++*switch_count;
    
    		context_switch(rq, prev, next); /* 负责执行底层的上下文切换 */
    		/*
    		 * The context switch have flipped the stack from under us
    		 * and restored the local variables which were saved when
    		 * this task called schedule() in the past. prev == current
    		 * is still correct, but it can be moved to another cpu/rq.
    		 */
    		cpu = smp_processor_id(); /* 更新信息 */
    		rq = cpu_rq(cpu);
    	} else
    		raw_spin_unlock_irq(&rq->lock);/* 如果依旧是原来的线程,则直接解锁 */
    
    	post_schedule(rq);
    
    	preempt_enable_no_resched();
    	if (need_resched()) /* 判断是否设置了重调度位,如果设置了则重新搜索新的进程 */
    		goto need_resched;
    }


  • 相关阅读:
    [C++] Class (part 2)
    [C++] Class (part 1)
    [c++] Inline Function
    [C++] in-class initializer
    简易线程池Thread Pool
    js里function的apply vs. bind vs. call
    webix custom component-九宫格
    webix源码阅读
    比特币的原理+问题
    wpf中UserControl的几种绑定方式
  • 原文地址:https://www.cnblogs.com/youngerchina/p/5624640.html
Copyright © 2020-2023  润新知