linux2.6调度分析
来源:互联网 发布:java 选择 编辑:程序博客网 时间:2024/05/01 22:07
/*首先我先说一下操作系统进程调度的重要性。我们都知道,linux的所有进程是并发执行的。进程调度,其实质是把处理器公平,合理,高效的分配给各个任务,以使进程的并发执行达到类似并行的效果。调度是实现多任务并发执行的必要手段。
负载指的是可运行进程的平均数,以及因此而等待CPU的时间。
进程运行时间的长短由它的优先级来确定,进程在最初执行的时候有一个静态优先级,同时在执行的时候还有一个动态优先级,它执行时间的长短由这些来确定.静态优先级是一确定就不变的,动态优先级会改变,它会受到你是交互性或者是消耗性这些性质的改变. */
struct prio_array {
int nr_active; /*本进程组中的进程数 */
unsigned long bitmap[BITMAP_SIZE]; /*位图,用来加速HASH表的访问*/
struct list_head queue[MAX_PRIO]; /*以优先级为索引的HASH表*/
};
/*linux2.6的调度函数里没有使用godness()来选择进程,而是多定义了一个就绪队列。
代码来自linux-2.6.0/kernel/sched.c 199行~215行*/
struct runqueue {
spinlock_t lock; /*runqueue 的自旋锁,当需要对 runqueue 进行操作时,仍然应该锁定,但这个锁定操作只影响一个 CPU 上的就绪队列,
因此,竞争发生的概率要小多了。*/
unsigned long nr_running, nr_switches, expired_timestamp,
nr_uninterruptible;/*nr_running就绪队列中可运行进程数,nr_switch是CPU进程切换的次数,expired_timestamp是最早发生进程
用完时间片的时间,nr_uninterruptible是曾经在runqueue但是现在处于 TASK_UNINTERRUPTIBLE
状态的进程数*/
task_t *curr, *idle; /*curr是本CPU正在运行的进程,idle是指向本 CPU 的 idle 进程(这里)*/
struct mm_struct *prev_mm;/*prev_mm是在进程却换工程中,保存正被替换的进程的地址空间*/
prio_array_t *active, *expired, arrays[2]; /*每个CPU的就绪队列按时间片是否用完分为两部分,active指向时间片没有用完,可调度
的就绪进程;expire指向时间片用完的就绪进程;arrays[2]是实际的优先级进程队列*/
int prev_cpu_load[NR_CPUS]; /*记录进行负载平衡时各个 CPU 上的负载状态*/
#ifdef CONFIG_NUMA
atomic_t *node_nr_running;
int prev_node_load[MAX_NUMNODES];/*这两个属性仅在 NUMA 体系结构下有效,记录各个 NUMA 节点上的就绪进程数和上一次负载平衡操作时的负载情况*/
#endif
task_t *migration_thread; /*指向本 CPU 的迁移进程。每个 CPU 都有一个核心线程用于执行进程迁移操作*/
struct list_head migration_queue; /*需要进行迁移的进程列表*/
atomic_t nr_iowait; /*曾经在runqueue但是现在正在等待I/O操作完成的进程数*/
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*2.6内核对task_struct也作了很大改进*/
/*由于task_struct很大,这里我只说一些简单的*/
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
/*state有以下六种进程状态*/
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define TASK_STOPPED 4
#define TASK_ZOMBIE 8
#define TASK_DEAD 16 /*已经退出且不需要父进程回收的进程*/
......
unsigned long long timestamp; /*进程发生调度事件的时间*/
int prio, static_prio; /*prio是优先级,相当于 2.4 中 goodness() 的计算结果,在 0~MAX_PRIO-1 之间
取值(MAX_PRIO 定义为 140),
其中 0~MAX_RT_PRIO-1 (MAX_RT_PRIO 定义为100)属于实时进程范围,
MAX_RT_PRIO~MX_PRIO-1 属于非实时进程。数值越大,表示进程优先级越小。
这样定义实时进程也总是优先于普通进程。
static_prio是静态优先级,进程初始时间片的大小仅决定于进程的静态优先级,
这一点不论是实时进程还是非实时进程都一样,不过实时进程的static_prio 不参与优先级计算。*/
int activated; /*表示进程因什么原因进入就绪态,这一原因会影响到调度优先级的计算。*/
unsigned long sleep_avg;/*进程的平均等待时间(以 nanosecond 为单位),在 0 到 NS_MAX_SLEEP_AVG 之间取值,
初值为 0,相当于进程等待时间与运行时间的差值。
这个值是动态优先级计算的关键因子,sleep_avg 越大,计算出来的进程优先级也越高(数值越小)。*/
unsigned int time_slice, first_time_slice;/*time_slice是进程剩余的时间片,相当于2.4的counter,但是不再直接影响动态优先级
first_time_slive取值0 或 1,表示是否是第一次拥有时间片(刚创建的进程)。
这一变量用来判断进程结束时是否应当将自己的剩余时间片返还给父进程*/
prio_array_t *array; /*记录当前 CPU 的活跃就绪队列*/
......
}
//代码来自2.6.0内核linux-2.6.0/kernel/sched.c
asmlinkage void schedule(void)
{
task_t *prev, *next; /*prev是调度前运行的进程,next是调度后运行的进程*/
runqueue_t *rq;
prio_array_t *array;
struct list_head *queue;
unsigned long long now;
unsigned long run_time;
int idx;
/*
* Test if we are atomic. Since do_exit() needs to call into
* schedule() atomically, we ignore that path for now.
* Otherwise, whine if we are scheduling when we should not be.
*/
if (likely(!(current->state & (TASK_DEAD | TASK_ZOMBIE)))) { /*判断当前进程状态是不是TASK_DEAD或TASK_ZOMBIE*/
if (unlikely(in_atomic())) {
printk(KERN_ERR "bad: scheduling while atomic!\n");
dump_stack();
}
}
need_resched:
preempt_disable(); /*关闭内核枪占*/
prev = current; /*把当前进程报存在prev内*/
rq = this_rq(); /*CPU相关的就绪队列的地址保存在rq中*/
release_kernel_lock(prev); /*释放内核锁*/
now = sched_clock();/* 调用sched_clock( ),读取TSC,并且将TSC转换成纳秒,得到的timestamp保存在now中,然后Schedule计算prev使用
的时间片。*/
if (likely(now - prev->timestamp <>
run_time = now - prev->timestamp;
else
run_time = NS_MAX_SLEEP_AVG;
/*
* Tasks with interactive credits get charged less run_time
* at high sleep_avg to delay them losing their interactive
* status
*/
if (HIGH_CREDIT(prev))
run_time /= (CURRENT_BONUS(prev) ? : 1);
spin_lock_irq(&rq->lock);/*关闭当前CPU中断,并且获取自旋锁保护runqueue.*/
/*
* if entering off of a kernel preemption go straight
* to picking the next task.
*/
if (unlikely(preempt_count() & PREEMPT_ACTIVE))
goto pick_next_task;
switch (prev->state) { /*根据prev的状态做相应的处理*/
case TASK_INTERRUPTIBLE: /*此状态表明该进程可以被信号中断*/
if (unlikely(signal_pending(prev))) { /*如果该进程有未处理的信号,则变为可运行状态*/
prev->state = TASK_RUNNING;
break;
}
default: /*如果是不可运行的,则从就绪队列中删除它*/
deactivate_task(prev, rq); /*删除prev指向的进程*/
prev->nvcsw++;
break;
case TASK_RUNNING: /*如果是运行态,则继续执行*/
prev->nivcsw++;
}
pick_next_task:
if (unlikely(!rq->nr_running)) {
#ifdef CONFIG_SMP
load_balance(rq, 1, cpu_to_node_mask(smp_processor_id()));/*保持多系统下runqueue平衡。检查当前CPU,保证一个域中runqueue
平衡。*/
if (rq->nr_running) /*如果有可运行的进程*/
goto pick_next_task;
#endif
next = rq->idle;
rq->expired_timestamp = 0;
goto switch_tasks;
}
/*下面的代码是将active指向的队列与expired指向的队列交换*/
array = rq->active;
if (unlikely(!array->nr_active)) {
/*
* Switch the active and expired arrays.
*/
rq->active = rq->expired;
rq->expired = array;
array = rq->active;
rq->expired_timestamp = 0;
}
idx = sched_find_first_bit(array->bitmap);/*找到第一个非空的就绪进程链表*/
queue = array->queue + idx;
next = list_entry(queue->next, task_t, run_list);/*next指向将替换prev的进程描述符*/
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (next->activated > 0) {//这里表示是从TASK_INTERRUPTIBLE的状态唤醒
unsigned long long delta = now - next->timestamp; //中间这一段我不明白,这些涉及都是时间方面的计算,这让我
//感觉好像迷路了一样。我感觉这应该是硬件方面的原因。
if (next->activated == 1)
delta = delta * (ON_RUNQUEUE_WEIGHT * 128 / 100) / 128;
array = next->array;
dequeue_task(next, array);
recalc_task_prio(next, next->timestamp + delta);
enqueue_task(next, array);
}
next->activated = 0;
switch_tasks:
prefetch(next);
clear_tsk_need_resched(prev);
RCU_qsctr(task_cpu(prev))++;
prev->sleep_avg -= run_time;
if ((long)prev->sleep_avg <= 0){
prev->sleep_avg = 0;
if (!(HIGH_CREDIT(prev) || LOW_CREDIT(prev)))
prev->interactive_credit--;
}
prev->timestamp = now;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (likely(prev != next)) {/*如果要运行的进程不是当前进程,执行进程切换*/
next->timestamp = now;
rq->nr_switches++;
rq->curr = next;
prepare_arch_switch(rq, next);
prev = context_switch(rq, prev, next);
barrier();
finish_task_switch(prev);
} else /*如果要运行的进程是当前进程,放弃切换*/
spin_unlock_irq(&rq->lock);
reacquire_kernel_lock(current);
preempt_enable_no_resched();
if (test_thread_flag(TIF_NEED_RESCHED))
goto need_resched;
}
/*对于2.6的这一进程调度的分析我仍然是很模糊的,这里我看到它和2.4的调度函数有些不一样,2.6没有goodness()函数,每一个CUP都有自己的就绪队列,活动的和不活动的。当活动的就绪队列为空时,只要将两个就绪队列进行交换,便可继续执行。
负载指的是可运行进程的平均数,以及因此而等待CPU的时间。
进程运行时间的长短由它的优先级来确定,进程在最初执行的时候有一个静态优先级,同时在执行的时候还有一个动态优先级,它执行时间的长短由这些来确定.静态优先级是一确定就不变的,动态优先级会改变,它会受到你是交互性或者是消耗性这些性质的改变. */
struct prio_array {
int nr_active; /*本进程组中的进程数 */
unsigned long bitmap[BITMAP_SIZE]; /*位图,用来加速HASH表的访问*/
struct list_head queue[MAX_PRIO]; /*以优先级为索引的HASH表*/
};
/*linux2.6的调度函数里没有使用godness()来选择进程,而是多定义了一个就绪队列。
代码来自linux-2.6.0/kernel/sched.c 199行~215行*/
struct runqueue {
spinlock_t lock; /*runqueue 的自旋锁,当需要对 runqueue 进行操作时,仍然应该锁定,但这个锁定操作只影响一个 CPU 上的就绪队列,
因此,竞争发生的概率要小多了。*/
unsigned long nr_running, nr_switches, expired_timestamp,
nr_uninterruptible;/*nr_running就绪队列中可运行进程数,nr_switch是CPU进程切换的次数,expired_timestamp是最早发生进程
用完时间片的时间,nr_uninterruptible是曾经在runqueue但是现在处于 TASK_UNINTERRUPTIBLE
状态的进程数*/
task_t *curr, *idle; /*curr是本CPU正在运行的进程,idle是指向本 CPU 的 idle 进程(这里)*/
struct mm_struct *prev_mm;/*prev_mm是在进程却换工程中,保存正被替换的进程的地址空间*/
prio_array_t *active, *expired, arrays[2]; /*每个CPU的就绪队列按时间片是否用完分为两部分,active指向时间片没有用完,可调度
的就绪进程;expire指向时间片用完的就绪进程;arrays[2]是实际的优先级进程队列*/
int prev_cpu_load[NR_CPUS]; /*记录进行负载平衡时各个 CPU 上的负载状态*/
#ifdef CONFIG_NUMA
atomic_t *node_nr_running;
int prev_node_load[MAX_NUMNODES];/*这两个属性仅在 NUMA 体系结构下有效,记录各个 NUMA 节点上的就绪进程数和上一次负载平衡操作时的负载情况*/
#endif
task_t *migration_thread; /*指向本 CPU 的迁移进程。每个 CPU 都有一个核心线程用于执行进程迁移操作*/
struct list_head migration_queue; /*需要进行迁移的进程列表*/
atomic_t nr_iowait; /*曾经在runqueue但是现在正在等待I/O操作完成的进程数*/
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*2.6内核对task_struct也作了很大改进*/
/*由于task_struct很大,这里我只说一些简单的*/
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
/*state有以下六种进程状态*/
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define TASK_STOPPED 4
#define TASK_ZOMBIE 8
#define TASK_DEAD 16 /*已经退出且不需要父进程回收的进程*/
......
unsigned long long timestamp; /*进程发生调度事件的时间*/
int prio, static_prio; /*prio是优先级,相当于 2.4 中 goodness() 的计算结果,在 0~MAX_PRIO-1 之间
取值(MAX_PRIO 定义为 140),
其中 0~MAX_RT_PRIO-1 (MAX_RT_PRIO 定义为100)属于实时进程范围,
MAX_RT_PRIO~MX_PRIO-1 属于非实时进程。数值越大,表示进程优先级越小。
这样定义实时进程也总是优先于普通进程。
static_prio是静态优先级,进程初始时间片的大小仅决定于进程的静态优先级,
这一点不论是实时进程还是非实时进程都一样,不过实时进程的static_prio 不参与优先级计算。*/
int activated; /*表示进程因什么原因进入就绪态,这一原因会影响到调度优先级的计算。*/
unsigned long sleep_avg;/*进程的平均等待时间(以 nanosecond 为单位),在 0 到 NS_MAX_SLEEP_AVG 之间取值,
初值为 0,相当于进程等待时间与运行时间的差值。
这个值是动态优先级计算的关键因子,sleep_avg 越大,计算出来的进程优先级也越高(数值越小)。*/
unsigned int time_slice, first_time_slice;/*time_slice是进程剩余的时间片,相当于2.4的counter,但是不再直接影响动态优先级
first_time_slive取值0 或 1,表示是否是第一次拥有时间片(刚创建的进程)。
这一变量用来判断进程结束时是否应当将自己的剩余时间片返还给父进程*/
prio_array_t *array; /*记录当前 CPU 的活跃就绪队列*/
......
}
//代码来自2.6.0内核linux-2.6.0/kernel/sched.c
asmlinkage void schedule(void)
{
task_t *prev, *next; /*prev是调度前运行的进程,next是调度后运行的进程*/
runqueue_t *rq;
prio_array_t *array;
struct list_head *queue;
unsigned long long now;
unsigned long run_time;
int idx;
/*
* Test if we are atomic. Since do_exit() needs to call into
* schedule() atomically, we ignore that path for now.
* Otherwise, whine if we are scheduling when we should not be.
*/
if (likely(!(current->state & (TASK_DEAD | TASK_ZOMBIE)))) { /*判断当前进程状态是不是TASK_DEAD或TASK_ZOMBIE*/
if (unlikely(in_atomic())) {
printk(KERN_ERR "bad: scheduling while atomic!\n");
dump_stack();
}
}
need_resched:
preempt_disable(); /*关闭内核枪占*/
prev = current; /*把当前进程报存在prev内*/
rq = this_rq(); /*CPU相关的就绪队列的地址保存在rq中*/
release_kernel_lock(prev); /*释放内核锁*/
now = sched_clock();/* 调用sched_clock( ),读取TSC,并且将TSC转换成纳秒,得到的timestamp保存在now中,然后Schedule计算prev使用
的时间片。*/
if (likely(now - prev->timestamp <>
run_time = now - prev->timestamp;
else
run_time = NS_MAX_SLEEP_AVG;
/*
* Tasks with interactive credits get charged less run_time
* at high sleep_avg to delay them losing their interactive
* status
*/
if (HIGH_CREDIT(prev))
run_time /= (CURRENT_BONUS(prev) ? : 1);
spin_lock_irq(&rq->lock);/*关闭当前CPU中断,并且获取自旋锁保护runqueue.*/
/*
* if entering off of a kernel preemption go straight
* to picking the next task.
*/
if (unlikely(preempt_count() & PREEMPT_ACTIVE))
goto pick_next_task;
switch (prev->state) { /*根据prev的状态做相应的处理*/
case TASK_INTERRUPTIBLE: /*此状态表明该进程可以被信号中断*/
if (unlikely(signal_pending(prev))) { /*如果该进程有未处理的信号,则变为可运行状态*/
prev->state = TASK_RUNNING;
break;
}
default: /*如果是不可运行的,则从就绪队列中删除它*/
deactivate_task(prev, rq); /*删除prev指向的进程*/
prev->nvcsw++;
break;
case TASK_RUNNING: /*如果是运行态,则继续执行*/
prev->nivcsw++;
}
pick_next_task:
if (unlikely(!rq->nr_running)) {
#ifdef CONFIG_SMP
load_balance(rq, 1, cpu_to_node_mask(smp_processor_id()));/*保持多系统下runqueue平衡。检查当前CPU,保证一个域中runqueue
平衡。*/
if (rq->nr_running) /*如果有可运行的进程*/
goto pick_next_task;
#endif
next = rq->idle;
rq->expired_timestamp = 0;
goto switch_tasks;
}
/*下面的代码是将active指向的队列与expired指向的队列交换*/
array = rq->active;
if (unlikely(!array->nr_active)) {
/*
* Switch the active and expired arrays.
*/
rq->active = rq->expired;
rq->expired = array;
array = rq->active;
rq->expired_timestamp = 0;
}
idx = sched_find_first_bit(array->bitmap);/*找到第一个非空的就绪进程链表*/
queue = array->queue + idx;
next = list_entry(queue->next, task_t, run_list);/*next指向将替换prev的进程描述符*/
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (next->activated > 0) {//这里表示是从TASK_INTERRUPTIBLE的状态唤醒
unsigned long long delta = now - next->timestamp; //中间这一段我不明白,这些涉及都是时间方面的计算,这让我
//感觉好像迷路了一样。我感觉这应该是硬件方面的原因。
if (next->activated == 1)
delta = delta * (ON_RUNQUEUE_WEIGHT * 128 / 100) / 128;
array = next->array;
dequeue_task(next, array);
recalc_task_prio(next, next->timestamp + delta);
enqueue_task(next, array);
}
next->activated = 0;
switch_tasks:
prefetch(next);
clear_tsk_need_resched(prev);
RCU_qsctr(task_cpu(prev))++;
prev->sleep_avg -= run_time;
if ((long)prev->sleep_avg <= 0){
prev->sleep_avg = 0;
if (!(HIGH_CREDIT(prev) || LOW_CREDIT(prev)))
prev->interactive_credit--;
}
prev->timestamp = now;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (likely(prev != next)) {/*如果要运行的进程不是当前进程,执行进程切换*/
next->timestamp = now;
rq->nr_switches++;
rq->curr = next;
prepare_arch_switch(rq, next);
prev = context_switch(rq, prev, next);
barrier();
finish_task_switch(prev);
} else /*如果要运行的进程是当前进程,放弃切换*/
spin_unlock_irq(&rq->lock);
reacquire_kernel_lock(current);
preempt_enable_no_resched();
if (test_thread_flag(TIF_NEED_RESCHED))
goto need_resched;
}
/*对于2.6的这一进程调度的分析我仍然是很模糊的,这里我看到它和2.4的调度函数有些不一样,2.6没有goodness()函数,每一个CUP都有自己的就绪队列,活动的和不活动的。当活动的就绪队列为空时,只要将两个就绪队列进行交换,便可继续执行。
0
上一篇:select函数用法详解(以例子剖析)
下一篇:linux2.6内核模块的编译
相关热门文章
- 7种不同性格的客户营销人应对...
- Linux内存分析(4) -- paging_i...
- 光明乳业更关心孩子的健康...
- Linux内存分析(3) -- create_...
- Linux内存分析(2) -- mm\bootm...
- linux 常见服务端口
- 【ROOTFS搭建】busybox的httpd...
- xmanager 2.0 for linux配置
- 什么是shell
- linux socket的bug??
- 初学UNIX环境高级编程的,关于...
- chinaunix博客什么时候可以设...
- 一个访问量较大网站的服务器。...
- 收音机驱动与v4l2架构的关系,...
- 如何将linux驱动改为裸机驱动(...
给主人留下些什么吧!~~
评论热议
0 0
- linux2.6调度分析
- linux2.6调度分析
- Linux2.6 内核进程调度分析
- Linux2.6 内核进程调度分析 .
- Linux2.6 内核进程调度分析
- linux2.6 CFS调度算法分析
- Linux2.6进程调度分析(1)-调度策略
- Linux2.6进程调度分析(2)-调度算法
- Linux2.6进程调度分析(1)-调度策略
- Linux2.6进程调度
- Linux2.6调度算法
- linux2.6调度器
- Linux2.6内核进程调度核心代码分析
- Linux2.6进程调度分析(3)-与调度有关的函数分析
- linux2.6.29 CFS调度详细分析(一)
- linux2.6.29 CFS调度详细分析(二)
- linux2.6.29 CFS调度详细分析(一)
- linux2.6.29 CFS调度详细分析(二)
- JavaScript之匿名函数和闭包
- 通过IOStat命令监控IO性能
- MFC 对话框最大化时控件也随比例最大化或者还原
- linux常用命令大全(根据自己学习经历整理,长期更新)
- 小白自学Android路之 xml文件的DOM解析编程
- linux2.6调度分析
- linux2.6内核模块的编译
- 悬挂运动控制系统设计报告
- 数组名问题
- 走进ARM-淡淡的记忆
- strncpy与memcpy
- Qt4的QDialog和QWidget
- 自己写的小ls
- 读者-写者实现
原创粉丝点击
热门IT博客
热门问题
老师的惩罚
人脸识别
我在镇武司摸鱼那些年
重生之率土为王
我在大康的咸鱼生活
盘龙之生命进化
天生仙种
凡人之先天五行
春回大明朝
姑娘不必设防,我是瞎子
中二病也要谈恋爱剧场版
中二病教你做人格格党
中二病也要谈恋爱第一季
中二病也要谈恋爱图片
二病也要谈恋爱
中二病也要谈恋爱take on me
中二病也要谈恋爱ed
中二病也要谈恋爱英语名
中二病也要谈恋爱免费观看
中二病日常说话方式
中二病少女要拯救世界
中二病也要谈恋爱qq皮肤
中二病谈恋爱动态图
中二病也要谈恋爱的英文名
中二病也要谈恋爱剧场版免费
中二病也要谈恋爱特典
中二病也要谈恋爱第三季
中二病也要谈恋爱 恋
草媹社地址一地址二地址三
番茄区社ios二维码
番茄区社app下载安卓二维码
番茄区社app邀请二维码
萝莉社第二季
甘肃人社认证二维码
萝球社第二季
二次元的咸鱼社 咸鱼社
外研社二年级英语上册安卓版
草橊社址2019 地址一地址二
二组词语
组词语二年级
动车组二等座座位图
二怎么组词语
了可以组什么词二个字的
二能组什么词
森永高中三年二组
中交二航院
吉大二院
河北省二院
徐州二院
省二院
广医二院