(笔记)Linux内核学习(三)之进程调度

进程调度:

在可运行态进程之间分配有限处理器时间资源的内核子系统。

一 调度策略

进程类型

I/O消耗型进程:大部分时间用来提交I/O请求或是等待I/O请求,经常处于可运行状态,但运行时间短,等待请求过程时处于阻塞状态。如交互式程序。

处理器消耗型进程:时间大都用在执行代码上,除非被抢占否则一直不停的运行。

综合型:既是I/O消耗型又是处理器消耗型。

调度策略要在:进程响应迅速(响应时间短)和最大系统利用率(高吞吐量)之间寻找平衡。

调度概念

优先级:基于进程价值和对处理器时间需求进行进程分级的调度。

时间片:表明进程被抢占前所能持续运行的时间,规定一个默认的时间片。时间片过长导致系统交互性的响应不好,

      程序并行性效果差;时间片太短增大进程切换带来的处理器耗时。矛盾!

时间片耗尽进程运行到期,暂时不可运行状态。直到所有进程时间片都耗尽,重新计算进程时间片。

Linux调度程序提高交互式程序优先级,提供较长时间片;实现动态调整优先级和时间片长度机制。

进程抢占:Linux系统是抢占式,始终运行优先级高的进程。

调度算法

可执行队列:runqueue;给定处理器上可执行进程的链表,每个处理器一个。每个可执行进程都唯一归属于一个可执行队列。

运行队列是调度程序中最基本的数据结构:

struct runqueue { 

  spinlock_t lock; /* 保护运行队列的自旋锁*/ 

  unsigned long nr_running; /* 可运行任务数目*/ 

  unsigned long nr_switches; /* 上下文切换数目*/ 

  unsigned long expired_timestamp; /* 队列最后被换出时间*/ 

  unsigned long nr_uninterruptible; /* 处于不可中断睡眠状态的任务数目*/ 

  unsigned long long timestamp_last_tick; /* 最后一个调度程序的节拍*/ 

  struct task_struct *curr; /* 当前运行任务*/ 

  struct task_struct *idle; /* 该处理器的空任务*/ 

  struct mm_struct *prev_mm; /* 最后运行任务的mm_struct结构体*/ 

  struct prio_array *active; /* 活动优先级队列*/ 

  atomic_t nr_iowait; /* 等待I/O操作的任务数目*/ 

  ……

};

提供了一组宏来获取给定CPU的进程执行队列:  

  #define cpu_rq(cpu)         //返回给定处理器可执行队列的指针

  #define this_rq()        //返回当前处理器的可执行队列

  #define task_rq(p)            //返回给定任务所在的队列指针

在操作处理器任务队列时候要用锁:

__task_rq_lock

……

__task_rq_unlock

4 schedule

系统要选定下一个执行的进程通过调用schedule函数完成。

调度时机:

  l  进程状态转换的时刻:进程终止、进程睡眠;

  l  当前进程的时间片用完时(current->counter=0);

  l  设备驱动程序调用;

  l  进程从中断、异常及系统调用返回到用户态时;

睡眠和唤醒:

休眠(被阻塞)的进程处于一个特殊的不可执行状态。休眠有两种进程状态:

    TASK_INTERRUPTIBLE:接收到信号就被唤醒

    TASK_UNINTERRUPTIBLE:忽略信号

  两种状态进程位于同一个等待队列上,等待某些事件,不能够运行。

进程休眠策略:

//q是我们希望睡眠的等待队列
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(q, &wait); 

//condition 是我们在等待的事件
while (!condition)
{
       //将进程状态设为不可执行休眠状态 or TASK_UNINTERRUPTIBLE
       set_current_state(TASK_INTERRUPTIBLE);

       if(signal_pending(current))
              //调度进程
              schedule();
}

//进程被唤醒条件满足 进程可执行状态
set_current_state(TASK_RUNNING); 

//将进程等待队列中移除
remove_wait_queue(q, &wait);

进程通过执行下面几个步骤将自己加入到一个等待队列中:

  1) 调用DECLARE_WAITQUEUE()创建一个等待队列的项。

  2) 调用add_wait_queue()把自己加入到队列中。该队列会在进程等待的条件满足时唤醒它。

    当然我们必须在其他地方撰写相关代码,在事件发生时,对等待队列执行wake_up()操作。

  3) 将进程的状态变更为 TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE。

  4) 如果状态被置为TASK_INTERRUPTIBLE,则信号唤醒进程。这就是所谓的伪唤醒(唤醒不是因为事件的发生),因此检查并处理信号。

  5) 检查条件是否为真;如果是的话,就没必要休眠了。如果条件不为真,调用schedule()。

  6) 当进程被唤醒的时候,它会再次检查条件是否为真。如果是,它就退出循环,如果不是,它再次调用schedule()并一直重复这步操作。

  7) 当条件满足后,进程将自己设置为TASK_RUNNING并调用remove_wait_queue()把自己移出等待队列。

二 抢占和上下文切换

进程切换schedule函数调用context_switch()函数完成以下工作:

    l  调用定义在<asm/mmu_context.h>中的switch_mm(),该函数负责把虚拟内存从上一个进程映射切换到新进程中。

    l  调用定义在<asm/system.h>中的switch_to(),该函数负责从上一个进程的处理器状态切换到新进程的处理器状态。

      这包括保存、恢复栈信息和寄存器信息。在前面看到schedule函数调用有很多种情况,完全依靠用户来调用不能达到

      很好的效果。内核需要判断什么时候调用schedule,内核提供了一个need_resched标志来表明是否需要重新执行一次调度:

    l  当某个进程耗尽它的时间片时,scheduler_tick()就会设置这个标志;

    l  当一个优先级高的进程进入可执行状态的时候,try_to_wake_up()也会设置这个标志。

  每个进程都包含一个need_resched标志,这是因为访问进程描述符内的数值要比访问一个全局变量快

  (因为current宏速度很快并且描述符通常都在高速缓存中)。

用户抢占

内核即将返回用户空间时候,如果need_resched标志被设置,会导致schedule函数被调用,此时发生用户抢占。

用户抢占在以下情况时产生:

    l  从系统调返回用户空间。

    l  从中断处理程序返回用户空间。

内核抢占

只要重新调度是安全的,那么内核就可以在任何时间抢占正在执行的任务。

什么时候重新调度才是安全的呢?只要没有持有锁,内核就可以进行抢占。锁是非抢占区域的标志。由于内核是支持SMP的,

所以,如果没有持有锁,那么正在执行的代码就是可重新导入的,也就是可以抢占的。

  为了支持内核抢占所作的第一处变动就是为每个进程的thread_info引入了preempt_count计数器。该计数器初始值为0,

每当使用锁的时候数值加1,释放锁的时候数值减1。当数值为0的时候,内核就可执行抢占。从中断返回内核空间的时候,

内核会检查need_resched和preempt_count的值。如果need_resched被设置,并且preempt_count为0的话,这说明

有一个更为重要的任务需要执行并且可以安全地抢占,此时,调度程序就会被调用。

内核抢占会发生在:

  l  当从中断处理程序正在执行,且返回内核空间之前。

  l  当内核代码再一次具有可抢占性的时候。

  l  如果内核中的任务显式的调用schedule()。

  l  如果内核中的任务阻塞(这同样也会导致调用schedule())。

时间: 2024-11-10 12:02:15

(笔记)Linux内核学习(三)之进程调度的相关文章

(笔记)Linux内核学习(九)之内核内存管理方式

一 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理 地址,通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 32位系统:页大小4KB 64位系统:页大小8KB 内核用相应的数据结构表示系统中的每个物理页: <linux/mm_types.h> struct page {} 内核通过这样的数据结构管理系统中所有的页,因此内核判断一个页是否空闲,谁有拥有这个页 ,拥有者可能是:用户空间进程.动态分配的内核数据.静态内核代码.页高速缓存-- 系统中

(笔记)Linux内核学习(十一)之I/O层和I/O调度机制

一 块I/O基本概念 字符设备:按照字符流的方式被有序访问的设备.如串口.键盘等. 块设备:系统中不能随机(不需要按顺序)访问固定大小的数据片(chunk 块)的设备. 如:硬盘.软盘.CD-ROM驱动器.闪存等.都是通过以安装文件系统的方式使用. 块设备的组成: 扇区:是块设备中最小的可寻址单元(常见大小512字节):是块设备的基本寻址和操作单元. 块:是文件系统最小逻辑可寻址单元,文件系统的抽象,只能通过块访问文件系统.通常包含多个扇区. 当一个块被调入内存时(读入后或等待写出时),它要存储

(笔记)Linux内核学习(七)之内核同步机制和实现方式

一 原子操作 指令以原子的方式执行--执行过程不被打断. 1 原子整数操作 原子操作函数接收的操作数类型--atomic_t //定义 atomic_t v;//初始化 atomic_t u = ATOMIC_INIT(0); //操作 atomic_set(&v,4); // v = 4 atomic_add(2,&v); // v = v + 2 = 6 atomic_inc(&v); // v = v + 1 = 7 //实现原子操作函数实现 static inline vo

(笔记)Linux内核学习(八)之定时器和时间管理

一 内核中的时间观念 内核在硬件的帮助下计算和管理时间.硬件为内核提供一个系统定时器用以计算流逝的时间.系 统定时器以某种频率自行触发,产生时钟中断,进入内核时钟中断处理程序中进行处理. 墙上时间和系统运行时间根据时钟间隔来计算. 利用时间中断周期执行的工作: 更新系统运行时间: 更新实际时间: 在smp系统上,均衡调度程序中各处理器上运行队列: 检查当前进程是否用尽了时间片,重新进行调度: 运行超时的动态定时器: 更新资源消耗和处理器时间的统计值: 二 节拍率 系统定时器的频率:通过静态预处理

(笔记)Linux内核学习(十)之虚拟文件系统概念

虚拟文件系统 虚拟文件系统:内核子系统VFS,VFS是内核中文件系统的抽象层,为用户空间提供文件系统相关接口: 通过虚拟文件系统,程序可以利用标准Linux文件系统调用在不同的文件系统中进行交互和操作. VFS作为抽象层: 文件系统被安装在一个特定的安装点上,该安装点在全局层次结构中被称作命名空间, 所有的已安装文件系统都作为根文件系统树的枝叶出现在系统中. 文件系统主要的对象: 超级块对象:代表一个已安装文件系统:struct super_block {} 索引节点对象:代表一个文件:stru

(笔记)Linux内核学习(六)之并发和同步概念

一 临界区和竞争条件 临界区:访问和操作共享数据的代码段. 竞争条件:多个执行线程处于同一个临界区中. 处于竞争条件:造成访问的数据或者资源不一致状态: 对资源i的访问:ProcessA和B访问后得到正确的结果应该是9: 进程是并发执行,有可能得到的结果是:8 防止这种情况的发生:保证对资源的访问原子操作. 二 加锁 锁:采用原子操作实现,原子操作不存在竞争. 造成并发原因: l  中断:随时可以打断当前执行的进程代码: l  软中断和tasklet:任何时刻能唤醒调度软中断和tasklet,打

操作系统 linux 内核的三种进程调度方法

1,SCHED_OTHER 分时调度策略: 2,SCHED_FIFO 实时调度策略,先到先服务: 3,SCHED_RR 实时调度策略,时间片轮转 . 实时进程将得到优先调用,实时进程根据实时优先级决定调度权值,分时进程则通过nice和counter值决定权值,nice越小,counter越大,被调度的概率越大,也就是曾经使用了 cpu 最少的进程将会得到优先调度. SHCED_RR 和 SCHED_FIFO 的不同: 当采用 SHCED_RR 策略的进程的时间片用完,系统将重新分配时间片,并置于

linux内核学习:进程调度

基本工作原理 只要有可以执行的进程,就一定有进程在执行: 如果可执行的进程数目多于CPU数目,就选择一个执行 调度类型 抢占式多任务 preemptive multitasking 调度器可以中断正在执行的进程,从而运行另一个进程 非抢占式多任务 cooperative multitasking 进程必须自己退出,其它进程才有可能运行 调度策略与进程特性 使用的调度策略往往和进程特性有关 系统响应速度与处理效率 高IO消耗型进程与高CPU消耗型进程 基于进程价值 更有价值或者说更重要的进程拥有更

Linux内核学习总结(final)

Linux内核学习总结 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这八周以来,我从拼不出来"Linux"这个词到知道了很多专有名词,也能大概了解Linux的工作机制,这一系列的进步都是一周周积累下来的.现在回过头来看,有种阳光总在风雨后的感觉,虽然这个比喻好像不太恰当. 闲话少说,接下来就进入这次的正题. 一.首先是对Linux操作系统的理解 1.操作系