进程调度函数schedule()分析

1、功能简述:

最主要作用就是

从就绪进程中选择一个优先级最高的进程来代替当前进程运行

2、代码分析

schedule();
      struct task_struct *tsk = current; //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();   //关闭内核抢占,关于内核抢占详见注释1

cpu = smp_processor_id();

rq = cpu_rq(cpu);   //跟当前进程相关的runqueue的信息被保存在rq中

rcu_note_context_switch(cpu);

prev = rq->curr;   //当前进程放入prev

schedule_debug(prev);

if (sched_feat(HRTICK))

hrtick_clear(rq);

raw_spin_lock_irq(&rq->lock);

switch_count = &prev->nivcsw;

 //如果内核态没有被抢占,并且内核抢占有效

if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {

//如果当前进程有非阻塞等待信号,并且它的状态是TASK_INTERRUPTIBLE

if (unlikely(signal_pending_state(prev->state, prev))) {

prev->state = TASK_RUNNING; //将当前进程的状态设为:TASK_RUNNING

} else {

deactivate_task(rq, prev, DEQUEUE_SLEEP);//将当前进程从runqueue(运行队列)中删除 

prev->on_rq = 0;  //标识当前进程不在runqueue中

 

//这里涉及到工作队列的知识,我们在以后的章节里在来说,这里略过

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))//如果runqueue中没有正在运行的进程

idle_balance(cpu, rq); //就会从其它CPU拉入进程

put_prev_task(rq, prev);   //通知调度器,当前进程要被另一个进程取代,做好准备

next = pick_next_task(rq); //从runqueue中选择最适合的进程

clear_tsk_need_resched(prev); //清除当前进程的重调度标识

rq->skip_clock_update = 0;

//当前进程与所选进程是否是同一进程,不属于同一进程才需要切换

if (likely(prev != next)) {

rq->nr_switches++;

rq->curr = next; //所选进程代替当前进程

++*switch_count;

context_switch(rq, prev, next); //负责底层上下文切换

cpu = smp_processor_id();

rq = cpu_rq(cpu);

} else

raw_spin_unlock_irq(&rq->lock);  //如果不需要切换进程,则只需要解锁

post_schedule(rq);

sched_preempt_enable_no_resched();

if (need_resched())

goto need_resched;

}

注释1:

 

内核抢占基础知识

1、内核抢占概念

当进程位于内核空间,有一个更高优先级的任务出现时,如果该内核支持抢占的话,则可以将当前任务挂起,执行更高优先级的任务!

2、用户抢占的概念


核即将返回用户空间的时候,如果need
resched标志被设置,会导致schedule()被调用,此时就会发生用户抢占。内核无论是在从中断处理程序还是在系统调用后返回,都会检查
need resched标志。如果它被设置了,那么,内核会选择一个其他(更合适的)进程投入运行。

3、内核抢占好处

首先,这是实时系统所要求的。试想一下,如果硬件中断开启了一个实时进程,如果内核不支持抢占的话,被开启的实时进程就要等到当前进程执行完毕才能被调度,这就带来了延时,对实时性不好!如果内核支持抢占的话,就可以将当前进程挂起,来执行实时进程,这样对实时性有利!

4、什么情况下不能抢占内核

(1)内核正进行中断处理

(2)内核正在进行中断上下文的Bottom Half(中断的底半部)处理

(3)内核的代码段正持有spinlock自旋锁、writelock/readlock读写锁等锁,处干这些锁的保护状态中。

(4)内核正在执行调度程序Scheduler,这种情况正对应我们的schedule函数分析!!!

(5)内核正在对每个CPU“私有”的数据结构操作


保证Linux内核在以上情况下不会被抢占,抢占式内核使用了一个变量preempt_count,称为内核抢占锁。这一变量被设置在进程的PCB结构
task_struct中。每当内核要进入以上几种状态时,变量preempt_
count就加1,指示内核不允许抢占。每当内核从以上几种状态退出时,变量preempt_ count就减1,同时进行可抢占的判断与调度。

时间: 2024-11-01 14:21:00

进程调度函数schedule()分析的相关文章

(转)进程调度函数schedule()解读

原文 在linux系统中,单处理器也是多线程处理信号.事件等.这就需要一个核心算法来进行进程调度.这个算法就是CFS(Completely Fair Scheduler).在 LInux Kernel Development 一书中用一句话总结CFS进程调度: 运行rbtree树中最左边叶子节点所代表的那个进程. 在一个自平衡二叉搜索树红黑树rbtree的树节点中,存储了下一个应该运行进程的数据.在这里我们看到了二叉搜索树的完美运用.具体可参见Introduction to Algorithms

理解进程调度时机跟踪分析进程调度与进程切换的过程

李洋 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这一次实验是针对linux系统中进程调度时机得深入理解. Linux 调度器将进程分为三类: 1. 交互式进程 2. 批处理进程 3. 实时进程 根据进程的不同分类 Linux 采用不同的调度策略.对于实时进程,采用 FIFO 或者 Round Robin 的调度策略.对于普通进程,则需要区分交互式和批处理式的不同.传统 Linux 

实验八——理解进程调度时机跟踪分析进程调度与进程切换的过程

理解进程调度时机跟踪分析进程调度与进程切换的过程 攥写人:李鹏举  学号:20132201 ( *原创作品转载请注明出处*) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000) 一.实验要求: 理解Linux系统中进程调度的时机,可以在内核代码中搜索schedule()函数,看都是哪里调用了schedule(),判断我们课程内容中的总结是否准确: 使用gdb跟踪分析一个schedule()函数 ,验

linux进程调度函数浅析(基于3.16-rc4)

众所周知,进程调度使用schedule()函数来完成,下面我们从分析该函数开始,代码如下(kernel/sched/core.c): 1 asmlinkage __visible void __sched schedule(void) 2 { 3 struct task_struct *tsk = current; 4 5 sched_submit_work(tsk); 6 __schedule(); 7 } 8 EXPORT_SYMBOL(schedule); 第3行获取当前进程描述符指针,存

Linux内核分析之理解进程调度时机跟踪分析进程调度与进程切换的过程

一.原理分析 1.调度时机 背景不同类型的进程有不同的调度需求第一种分类I/O-bond:频繁的进行I/O:通常会花费很多时间等待I/O操作的完成CPU-bound:计算密集型:需要大量的CPU时间进行运算 第二种分批处理进程(batch process):不必与用户交互,通常在后台运行:不必很快响应.典型的批处理程序:编译程序.科学计算实时进程(real-time process):有实时需求,不应被低优先级的进程阻塞:响应时间要短.要稳定.典型的实时进程:视频/音频.机械控制等交互式进程(i

linux内核分析第八周-理解进程调度时机跟踪分析进程调度与进程切换的过程

实验原理: 一.调度时机 不同类型的进程有不同的调度需求 第一种分类: I/O-bound 频繁的进行I/O 通常会花费很多时间等待I/O操作的完成 CPU-bound 计算密集型 需要大量的CPU时间进行运算 第二种分类 批处理进程(batch process) 不必与用户交互,通常在后台运行 不必很快响应 典型的批处理程序:编译程序.科学计算 实时进程(real-time process) 有实时需求,不应被低优先级的进程阻塞 响应时间要短.要稳定 典型的实时进程:视频/音频.机械控制等 交

常用hash函数对比分析(一)

主要目标:寻找一个hash函数,高效的支持64位整数运算,使得在速度.空间等效率相对其它函数函数较高,以及内部运算时32位整数运算. 测试了"RSHash","JSHash","PJWHash","ELFHash","BKDRHash","SDBMHash","DJBHash","DEKHash","BPHash","

device_create 函数详细分析

原文地址:device_create 函数详细分析 作者:liujunwei1234 我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev. 内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)

(转)x264源码分析(1):main、parse、encode、x264_encoder_open函数代码分析

转自:http://nkwavelet.blog.163.com/blog/static/2277560382013103010312144/ x264版本:   x264-snapshot-20140226-2245  1.     首先对主函数进行分析,main函数很简洁,主要有三个步骤,见下图: 2.   接下来分析一下Parse函数中的主要过程: static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t