[进程管理] 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; }
posted on 2011-12-27 23:37 YoungerChina 阅读(563) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人