linux进程调度
2014-12-21 02:03
239 查看
本博文主要从三个方面简单介绍下linux进程调度,调度时机,调度策略,调度步骤三个方面简单介绍下linux系统额进程调度
1 . 什么是调度
调度就是在一组可运行状态的进程中选择一个最合适的进程来执行,这个是调度程序所要完成的基本操作
2. 调度策略
SCHED_NORMAL(SCHED_OTHER): 普 通 的 分时 进程
SCHED_FIFO : 先 入 先出 的 实 时 进程
SCHED_RR : 时 间 片 轮转 的 实 时 进程
SCHED_BATCH: 批 处理 进程
SCHED_IDLE: 只 在 系统空 闲 时 才能 够 被调度执行 的
3.调度类
linux内核将上面的5种调度方式划分成两张调度类
调度 类 的 引 入 增强 了 内核 调度程序 的 可 扩展性 , 这 些类( 调度程序 模 块 ) 封装 了调度 策略 ,并将 调度 策略模 块 化
CFS 调度 类 (在 在 kernel/sched_fair.c 中实 现 ) 用 于以 下 调度 策略 :
SCHED_NORMAL 、SCHED_BATCH 和 和 SCHED_IDLE 。
实 时 调度 类 (在 在 kernel/sched_rt.c 中实 现 ) 用 于以 下 调度 策略 :
SCHED_RR 和 和 SCHED_FIFO 策略 。
备注:struct sched_class 是调度类结构体 ,其中 pick_next_task : 选择下 一 个 要 运 行 的进程,调度策略在这个函数里面体现出来
4.调度时机
1 、 主 动式
在内核 中 直 接调 用schedule() 这个函数来实现进程调度。比如在下面这种情况需要主动式调度, 当 进程 需 要 等待资 源 等 而暂 时 停 止 运 行 时 , 会把 状态 置于 挂起(睡眠) ,并 主 动 请求 调度 , 让 出CPU 。
主 动 放 弃cpu 例:
current->state = TASK_INTERRUPTIBLE; //将进程设置为可中断状态,此时就不会被调度程序唤醒
schedule();
2、 被 动式 ( 抢占)
用 户 抢占 :Linux2.4 、Linux2.6版本内核支持用户抢占
内核 抢占:Linux2.6支持内核抢占,Linux2.4内核不支持内核抢占
2.1用户抢占
用 户 抢占 发 生 在:
(1)从 系统调 用 返 回 用 户空间 。
(2)从 中 断处理 程序 返 回 用 户空间
也就是在内核 即 将 返 回 用 户空间 的 时 候 , 如 果need_resched 标志 被 设置 , 会导 致schedule()被调 用, 此时就 会 发 生 用 户 抢占 。
用户抢占汇编代码分析
ENTRY(ret_from_exception) // 异常 返 回
get_thread_info tsk
mov why, #0
b ret_to_user
2.2 内核抢占
(1)在不 支持 内核 抢占 的 系统中 , 进程/ 线程一 旦运 行 于内核 空间 , 就 可以一 直 执行 , 直 到 它 主 动 放 弃 或 时 间片 耗尽 为止 。 这 样 一 些 非 常 紧急 的 进程 或 线程 将 长 时间 得不 到 运 行
(2) 在 支持 内核 抢占 的 系统中 , 更高 优先级 的 进程/ 线程可以 抢占 正 在 内核 空间 运 行 的 低 优先级 进程/
2.3内核抢占时机
在 支持 内核 抢占 的 系统中 , 某 些特例下 是 不 允许 内核 抢占 的
1) 内核 正进行中 断处理 。 进程调度 函 数schedule() 会对 此 作 出 判 断 , 如 果 是 在中 断 中调 用, 会 打 印 出 错
2) 内核 正 在 进行中 断 上 下 文 的Bottom Half( 中 断 的 底半部) 处理 。 硬 件 中 断 返 回前会 执行 软 中 断 , 此时仍 然 处于 中 断 上 下 文中
3) 进程 正 持 有spinlock 自旋锁 、writelock/readlock 读写锁 等 , 当 持 有 这 些 锁时 ,不应该 被 抢占 , 否 则 由于 抢占 将 导 致 其 他CPU 长 期 不 能 获 得 锁 而 死等
4) 内核 正 在 执行调度程序Scheduler 。 抢占 的 原因就 是为了进行 新 的 调度 , 没有 理由 将 调度程序 抢占 掉再 运 行调度程序
抢占时机判断:
为 保 证Linux 内核在 以 上 情况 下 不 会 被 抢占 ,抢占 式内核使用 了一 个 变 量preempt_count ,称 为 内核 抢占 计 数 。 这一 变 量被 设置 在 进程 的thread_info 结构中 。 每 当 内核 要进 入 以 上 几
种 状态 时 , 变 量preempt_count 就 加1 , 指 示内核不 允许抢占 。 每 当 内核从 以 上 几种 状态 退出时 , 变 量preempt_count 就 减1 , 同时 进行可 抢占 的 判 断 与调度,只要
preempt_count 不为0就不可被抢占。
内核抢占发生时机:
1)中 断处理 程序 完 成, 返 回 内核 空间 之前 。
2) 当 内核 代码 再 一 次具 有 可 抢占性 的 时候 , 如 解锁 及 使 能 软 中 断 等
代码示例:
中断返回时候:
3. 调度标志(TIF_NEED_RESCHED)
作 用:内核 提 供了一 个need_resched 标志 来 表 明 是 否 需 要重 新执行一 次 调度 。
设置 :当 某 个 进程 耗尽 它 的 时 间 片时 , 会 设置 这 个 标志 ;当 一 个 优先级 更高 的 进程进 入 可执行状态 的 时 候 , 也 会 设置 这 个 标志
4.调度步骤
Schedule 函 数 工作 流 程 如下 :
1). 清 理当 前运 行中 的 进程 ;
2). 选择下 一 个 要 运 行 的 进程 ;(pick_next_task 函数会在所有TASK_RUNNING进程里面选择出一个进程)
/*
* Pick up the highest-prio task:
*/
static inline struct task_struct * pick_next_task(struct rq *rq)
{
const struct sched_class *class;
struct task_struct *p;
/*
* Optimization: we know that if all tasks are in
* the fair class we can call that function directly:
*/
if (likely(rq->nr_running == rq->cfs.nr_running)) { //如果nr_running==cfs.nr_running,则说明当前rq队列中是没有rt任务的,rt任务不在cfs队列中,它的优先级的设置和cfs不一样。
p = fair_sched_class.pick_next_task(rq); //在cfs队列中挑选即将被切换进去的进程
if (likely(p))
return p;
}
for_each_class(class) { //如果有其他方式的调度类也就是不止是只有cfs调度类,那么依次按照优先级由高到低使用调度类
p = class->pick_next_task(rq);//使用该调度来了获取最合适的进程
if (p)
return p;
}
BUG(); /* the idle class will always have a runnable task */
}
3). 设置新 进程 的 运 行 环境;
4). 进程 上 下 文 切 换 。
1 . 什么是调度
调度就是在一组可运行状态的进程中选择一个最合适的进程来执行,这个是调度程序所要完成的基本操作
2. 调度策略
SCHED_NORMAL(SCHED_OTHER): 普 通 的 分时 进程
SCHED_FIFO : 先 入 先出 的 实 时 进程
SCHED_RR : 时 间 片 轮转 的 实 时 进程
SCHED_BATCH: 批 处理 进程
SCHED_IDLE: 只 在 系统空 闲 时 才能 够 被调度执行 的
3.调度类
linux内核将上面的5种调度方式划分成两张调度类
调度 类 的 引 入 增强 了 内核 调度程序 的 可 扩展性 , 这 些类( 调度程序 模 块 ) 封装 了调度 策略 ,并将 调度 策略模 块 化
CFS 调度 类 (在 在 kernel/sched_fair.c 中实 现 ) 用 于以 下 调度 策略 :
SCHED_NORMAL 、SCHED_BATCH 和 和 SCHED_IDLE 。
实 时 调度 类 (在 在 kernel/sched_rt.c 中实 现 ) 用 于以 下 调度 策略 :
SCHED_RR 和 和 SCHED_FIFO 策略 。
备注:struct sched_class 是调度类结构体 ,其中 pick_next_task : 选择下 一 个 要 运 行 的进程,调度策略在这个函数里面体现出来
4.调度时机
1 、 主 动式
在内核 中 直 接调 用schedule() 这个函数来实现进程调度。比如在下面这种情况需要主动式调度, 当 进程 需 要 等待资 源 等 而暂 时 停 止 运 行 时 , 会把 状态 置于 挂起(睡眠) ,并 主 动 请求 调度 , 让 出CPU 。
主 动 放 弃cpu 例:
current->state = TASK_INTERRUPTIBLE; //将进程设置为可中断状态,此时就不会被调度程序唤醒
schedule();
2、 被 动式 ( 抢占)
用 户 抢占 :Linux2.4 、Linux2.6版本内核支持用户抢占
内核 抢占:Linux2.6支持内核抢占,Linux2.4内核不支持内核抢占
2.1用户抢占
用 户 抢占 发 生 在:
(1)从 系统调 用 返 回 用 户空间 。
(2)从 中 断处理 程序 返 回 用 户空间
也就是在内核 即 将 返 回 用 户空间 的 时 候 , 如 果need_resched 标志 被 设置 , 会导 致schedule()被调 用, 此时就 会 发 生 用 户 抢占 。
用户抢占汇编代码分析
ENTRY(ret_from_exception) // 异常 返 回
get_thread_info tsk
mov why, #0
b ret_to_user
? __irq_usr: // 在用 户态 收到 中 断 usr_entry kuser_cmpxchg_check …… …… …… b ret_to_user
ENTRY(ret_to_user) ret_slow_syscall: disable_irq @ disable interrupts ldrr1, [tsk, #TI_FLAGS] tstr1, #_TIF_WORK_MASK bne work_pending<pre name="code" class="html">work_pending: tstr1, #_TIF_NEED_RESCHED//<span style="font-size: 17.77777862548828px;">need_resched 标志 </span><span style="font-family: Arial, Helvetica, sans-serif;">是否</span><span style="font-family: Arial, Helvetica, sans-serif;">被 设置 </span> bne work_resched work_resched: bl schedule
2.2 内核抢占
(1)在不 支持 内核 抢占 的 系统中 , 进程/ 线程一 旦运 行 于内核 空间 , 就 可以一 直 执行 , 直 到 它 主 动 放 弃 或 时 间片 耗尽 为止 。 这 样 一 些 非 常 紧急 的 进程 或 线程 将 长 时间 得不 到 运 行
(2) 在 支持 内核 抢占 的 系统中 , 更高 优先级 的 进程/ 线程可以 抢占 正 在 内核 空间 运 行 的 低 优先级 进程/
2.3内核抢占时机
在 支持 内核 抢占 的 系统中 , 某 些特例下 是 不 允许 内核 抢占 的
1) 内核 正进行中 断处理 。 进程调度 函 数schedule() 会对 此 作 出 判 断 , 如 果 是 在中 断 中调 用, 会 打 印 出 错
2) 内核 正 在 进行中 断 上 下 文 的Bottom Half( 中 断 的 底半部) 处理 。 硬 件 中 断 返 回前会 执行 软 中 断 , 此时仍 然 处于 中 断 上 下 文中
3) 进程 正 持 有spinlock 自旋锁 、writelock/readlock 读写锁 等 , 当 持 有 这 些 锁时 ,不应该 被 抢占 , 否 则 由于 抢占 将 导 致 其 他CPU 长 期 不 能 获 得 锁 而 死等
4) 内核 正 在 执行调度程序Scheduler 。 抢占 的 原因就 是为了进行 新 的 调度 , 没有 理由 将 调度程序 抢占 掉再 运 行调度程序
抢占时机判断:
为 保 证Linux 内核在 以 上 情况 下 不 会 被 抢占 ,抢占 式内核使用 了一 个 变 量preempt_count ,称 为 内核 抢占 计 数 。 这一 变 量被 设置 在 进程 的thread_info 结构中 。 每 当 内核 要进 入 以 上 几
种 状态 时 , 变 量preempt_count 就 加1 , 指 示内核不 允许抢占 。 每 当 内核从 以 上 几种 状态 退出时 , 变 量preempt_count 就 减1 , 同时 进行可 抢占 的 判 断 与调度,只要
preempt_count 不为0就不可被抢占。
内核抢占发生时机:
1)中 断处理 程序 完 成, 返 回 内核 空间 之前 。
2) 当 内核 代码 再 一 次具 有 可 抢占性 的 时候 , 如 解锁 及 使 能 软 中 断 等
代码示例:
中断返回时候:
__irq_svc: /* 内核 态接 收到 中 断:中断入口*/ svc_entry 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 /* 进 入 中 断 , 抢占 计 数 加1*/ #ifdef CONFIG_PREEMPT get_thread_info tsk ldr r8, [tsk, #TI_PREEMPT] @ get preempt count add r7, r8, #1 @ increment it str r7, [tsk, #TI_PREEMPT] #endif irq_handler /* 中 断处理*/ #ifdef CONFIG_PREEMPT str r8, [tsk, #TI_PREEMPT] @ restore preempt count //获得<span style="font-size: 17.77777862548828px;">preempt_count 计数</span> ldr r0, [tsk, #TI_FLAGS] @ get flags teq r8, #0 @ if preempt count != 0//判断<span style="font-family: Arial, Helvetica, sans-serif;">preempt_count 计数</span> movne r0, #0 @ force flags to 0 tst r0, #_TIF_NEED_RESCHED blne svc_preempt #endif
svc_preempt: mov r8, lr 1: bl preempt_schedule_irq /* 调度*/ ldr r0, [tsk, #TI_FLAGS] tst r0, #_TIF_NEED_RESCHED moveqpc, r8
/*自旋锁解锁的时候*/
<span style="font-size: 18px; font-family: Arial, Helvetica, sans-serif;">define preempt_enable() \</span>
do { \ preempt_enable_no_resched(); \ barrier(); \ preempt_check_resched(); \ } while (0)
#define preempt_enable_no_resched() \ do { \ barrier(); \ dec_preempt_count(); \ /* 抢占 计 数 减 一*/ } while (0)
#define preempt_check_resched() \ do { \ if (unlikely(test_thread_flag(TIF_NEED_RES CHED))) \ preempt_schedule(); \ /* 调度*/ } while (0)
void __lockfunc _spin_unlock(spinlock_t*lock) { <span style="white-space:pre"> </span>spin_release(&lock->dep_map, 1,_RET_IP_); <span style="white-space:pre"> </span>_raw_spin_unlock(lock); <span style="white-space:pre"> </span>preempt_enable(); }
3. 调度标志(TIF_NEED_RESCHED)
作 用:内核 提 供了一 个need_resched 标志 来 表 明 是 否 需 要重 新执行一 次 调度 。
设置 :当 某 个 进程 耗尽 它 的 时 间 片时 , 会 设置 这 个 标志 ;当 一 个 优先级 更高 的 进程进 入 可执行状态 的 时 候 , 也 会 设置 这 个 标志
4.调度步骤
Schedule 函 数 工作 流 程 如下 :
1). 清 理当 前运 行中 的 进程 ;
2). 选择下 一 个 要 运 行 的 进程 ;(pick_next_task 函数会在所有TASK_RUNNING进程里面选择出一个进程)
/*
* Pick up the highest-prio task:
*/
static inline struct task_struct * pick_next_task(struct rq *rq)
{
const struct sched_class *class;
struct task_struct *p;
/*
* Optimization: we know that if all tasks are in
* the fair class we can call that function directly:
*/
if (likely(rq->nr_running == rq->cfs.nr_running)) { //如果nr_running==cfs.nr_running,则说明当前rq队列中是没有rt任务的,rt任务不在cfs队列中,它的优先级的设置和cfs不一样。
p = fair_sched_class.pick_next_task(rq); //在cfs队列中挑选即将被切换进去的进程
if (likely(p))
return p;
}
for_each_class(class) { //如果有其他方式的调度类也就是不止是只有cfs调度类,那么依次按照优先级由高到低使用调度类
p = class->pick_next_task(rq);//使用该调度来了获取最合适的进程
if (p)
return p;
}
BUG(); /* the idle class will always have a runnable task */
}
3). 设置新 进程 的 运 行 环境;
4). 进程 上 下 文 切 换 。
相关文章推荐
- Linux进程调度
- linux进程调度
- Linux进程调度(3):进程切换分析
- 关于linux进程调度的公平性
- linux进程调度
- Linux进程调度(1):CFS调度器的设计框架
- Linux进程调度
- Linux进程调度
- Linux进程调度(3):进程切换分析
- [Linux内核设计与实现]Linux进程调度
- linux进程调度
- Linux进程调度(1):CFS调度器的设计框架
- Linux进程调度和切换过程分析
- Linux进程调度
- Linux进程调度(1):CFS调度器的设计框架
- linux进程调度
- linux进程调度策略(SCHED_OTHER,SCHED_FIFO,SCHED_RR)
- 浅谈linux进程调度
- linux进程调度 - 进程调度初始化与运行队列
- linux进程调度