20135302魏静静——课本第4章学习笔记

第4章   进程调度

  • 调度:调度是一个平衡的过程。一方面,它要保证各个运行的进程能够最大限度的使用CP;另一方面,保证各个进程能公平的使用CPU。
  • 调度功能:决定哪个进程运行以及进程运行多长时间。
  • 调度实现原理:与进程的优先级有关
  • Linux上调度实现的方法:O(1)的调度算法
  • 调度相关的系统调用

4.1多任务

     多任务系统可以划分为两类:非抢占式多任务( cooperative multitasking )和抢占式多任务(preemptive multitasking)。像所 有Unix 的变体和许多其他现代操作系统一样, Linux 提供了抢占式的多任务模式。在此模式下,由调度程序来决定什么时候停止一个进程的运行, 以便其他进程能够得到执行机会.这个强制的挂起动作就叫做抢占( preemption ).进程在被抢占之前能够运行的时间是预先设置好的,而且有一 个专门的名字,叫进程的时间片( timeslice )。时间片实际上就是分配给每个可运行进程的处理器时间段。有效管理时间片能使调度程序从系统 全局的角度做出调度决定,这样做还可以避免个别进程独占系统资源.当今众多现代操作系统对程序运行都采用了动态时间片计算的方式,并且引入 了可配置的计算策略.不过我们将看到, Linux 姐一无二的“公平”调度程度本身并没有采取时间片来达到公平调度。

4.2Linux的进程调度

0(1)调度程序一一它是因为其算泌的行为而得名的θ 。它解决了先前版本Linux 调度程序的许多不足,引入了许多强大的新特性和性能特征。

这里主要要感谢静态时间片算法和针对每一处理器的运行队列,它们帮助我们摆脱了先前调度程序设计上的限制。
      0(1)调度器虽然在拥有数以十计(不是数以百计〉的多处理器的环境下尚能表现出近乎完美的性能和可扩展性,但是时间证明该调度算提对于

调度那些响应时间敏感的程序却有一些先天不足。这些程序我们称其为交互进程一一它无疑包括了所有需要用户交互的程序。

4.3策略

 策略决定调度程序在何时让什么进程运行

  • 1/0 消耗型和处理器消耗型的进程

    •  进程可以被分为uo 消耗型和处理器消耗型。前者指进程的大部分时间用来提交1/0 请求或
      是等待ν0 请求。处理器艳费型进程把时间大多用在执行代码上。除非被抢占,否则它们通常都一直
      不停地运行,因为它们没有太多的1/0 需求。
    • 调度策略通常要在两个矛盾的目标中间寻找平衡:进程响应迅速(响应时间短〉和最大系统
      利用率(高吞吐量)。
  • 进程优先级
    • nice 值,色的范围是从-20 到+19,默认值为0 :越大的nice 值意味着更低的优先级
    • 实时优先级,其值是可配置的,默认情况下它的变化范围是从0 到99 (包括0和99 )。与nice 值意义相反,越高的实时优先级数值意味着进程优先级越高.

4.4Linux调度算法

  •  调度器类
    • Linux 调度器是以模块方式提供的,这样做的目的是允许不同类型的进程可以有针对性地选
      择调度算哉。
      这种模块化结构被称为调度器类( scheduler classes ),它允许多种不同的可动态添加的调度
      算能并存,调度属于自己范畴的进程。每个调度器都有-个优先级,基础的调度器代码定义在
      kemel/scbed.c 文件中,它会按照优先级顺序遍历调度类,拥有一个可执行进程的最高优先级的调
      度器类胜出,去选择下面要执行的那一个程序。
      完全公平调度( CFS )是一个针对普通进程的调度类,在Linux 中称为SCHED_NORMAL
      〈在POSIX 中称为SCHED_OTHER) , CFS 算捧实现定义在文件kemel/scbed_ fair.c 。

4.5Linux调度的实现

  • 时间记账

    • CFS 不再有时间片的概念,但是它也必须维护每个进程运行的时间记账,因为它需要确保
      每个进程只在公平分配给它的处理器时间内运行。CFS 使用调度器实体结构(定义在文件<linux/
      sched.h>的struct_sched _entity 中)来追踪进程运行记账
    • CFS 使用vruntime 变量来记录一个程序到底运行了多长时间以及它还应该再运行多久。
      定义在kemeVsched_fair.c 文件中的update_curr()函数实现了该记账功能。
    • update_ currO 计算了当前进程的执行时间,并且将其存放在变量delta_exec 中。然后它又将
      运行时间传递给了一up也te_curr(),由后者再根据当前可运行进程总数对运行时间进行加权计
      算。最终将上述的权重值与当前运行进程的vruntime 相加。
  • 进程选择
    • CFS 的进程选择算法可简单总结为“运行rbtr回树中最左边叶子节点所代表的那个
      进程”。实现这一过程的函数是_pick_next_ entityQ,它定义在文件kemel/sched_剑”中。
    • 如何将进程加入rbtree 中,enqueue_entity()函数实现了这一目的
  • 调度器入口
    • 进程调度的主要入口点是函数schedule(),它定义在文件kemel/sched .c 中。它正是内核其他
      部分用子调用进程调度器的入口。它会调用pick_next_task() (也定义在文件kernel/sched.c 中)。pick_
      next_task()会以优先级为序,从高到低,依次检查每一个调度类,并且从最高优先级的调度’类
      中,选择最高优先级的进程。
  • 睡眠和唤醒
    • 休眠〈被阻塞)的进程处于一个特殊的不可执行状态。
    • 休眠通过等待队列进行处理。等待队列是由等待某些事件发生的进程组成的简单链表。内
      核用wake_queue_ head _t 来代表等待队列。等待队列可以通过DECL成立WAITQUEUE()静态创
      建,也可以由init_waitqueue _head()动态创建。
    • 进程通过执行下面几个步骤将自己加入到一个等待队列中
      • 1 )调用宏DEF刷E_WAIT()创建一个等待队列的项。
        2 )调用add_wai!_ queue()把自己加入到队列中。该队列会在进程等待的条件摘足时唤醒它。
        当然我们必须在其他地方撰写相关代码,在事件发生时,对等待队列执行wake_u以)操作。
        3 )调用prepare_to_ wait()方站将进程的状态变更为TASK_剧TERRUPTIBLE 或TASK_
        UNINTERRUPTIBLE。而且该函数如果有必要的话会将进程加回到等待队列,这是在接下来的
        循环遍历中所需要的。
        4 )如果状态被设置为TASK_INTERRUPTIBLE ,贝I]信号唤醒进程。这就是所谓的伪唤醒
        〈唤醒不是因为事件的发生),因此检查并处理倍号。
        5 )当进程被唤醒的时候,它会再次检查条件是否为真。如果是,它就退出循环:如果不
        是,它再次调用schedule()井一直重复这步操作。
        6)当条件满足后,进程将自己设置为TASK_RUNNING 并调用finish_wait()方挫把自己移
        出等待队列。
        如果在进程开始休眠之前条件就已经达成了,那么循环会退出,进程不会存在错误地进入休
        眠的倾向。需要注意的是,内核代码在循环体内常常需要完成一些其他的任务,比如,它可能在
        调用schedule()之前需要释放掉锁,而在这以后再重新获取它们,或者响应其他事件。
        函数inotify_r,臼d(),位于文件fs/notify/inotify/inotify_ user.c 中,负责从通知文件描述符中读
        取信息。
    • 唤醒操作通过函数wake_upO 进行,它会唤醒指定的等待队列上的所有进程。它调用函
      数衍y_to_wake_upO,该函数负责将进程设置为TASK_RUNNING 状态,调用enqueue_task()将
      此进程放入红黑树中。

4.6抢占和上下文切换

         上下文切换,也就是从一个可执行进程切换到另一个可执行进程,由定义在kernel/ sched.c 中
的context_switch()函数负责处理.每当一个新的进程被选出来准备投入运行的时候, schedule()
就会调用该函数。它完成了两项基本的工作:
                   ·调用声明在<asm/mmu_ context.h>中的switch_mm(), 该函数负责把虚拟内存从上一个

                     进程映射切换到新进程中。
                   ·调用声明在<asm/system.h> 中的switch_to(),

该函数负责从上一个进程的处理器状态切换到新进程的处理器状态。这包括保存、恢复检信息和寄存器信息,还有

其他任何与体系结构相关的状态信息,都必须以每个进程为对象进行管理和保存。_add_w血’_queue衍巴任务

加到等待队列中.然后调用schedule()

  • 用户抢占

    • 内核即将返回用户空间的时候,如果need_resched 标志被设置,会导致scheduleO 被调用,
      此时就会发生用户抢占。在内核返回用户空间的时候,它知道自己是安全的,因为既然它可以继
      续去执行当前进程,那么它当然可以再去选择一个新的进程去执行。所以,内核无论是在中断处
      理程序还是在系统调用后返回,都会检查need_resched 标志。如果它被设置了,那么,内核会选
      择一个其他〈更合适的)进程投入运行。从中断处理程序或系统调用返回的返回路径都是跟体系
      结构相关的,在en町.s (此文件不仅包含内核入口部分的程序,内核退出部分的相关代码也在其
      巾)文件中通过汇编语言来实现。
      简而言之,用户抢占在以下情况时产生
      .从系统调返回用户空间时.
      ·从中断处理程序返回用户空间时。
  • 内核抢占
    • 内核抢占会发生在2
      ·中断处理程序正在执行,且返回内核空间之前.
      ·内核代码再一次具有可抢占性的时候。
      ·如果内核中的任务显式地调用schedule() o
      ·如果内核中的任务阻塞(这同样也会导敖调用schedule() )。

4.7实时调度策略

  • Linux 提供了两种实时调度策略: SCI租D_FIFO 和SCHED_RR。
  1. SCHED_FIFO 实现了一种简单的、先入先出的调度算怯:它不使用时间片.处于可运行
    状态的SCHED_FIFO 级的进程会比任何SCHED_NORMAL 级的进程都先得到调度。一且一个
    SCI盟D_FIFO 级进程处于可执行状态,就会一直执行,直到它自己受阻塞或显式地释放处理器
    为止;它不基于时间片,可以一直执行下去.只有更高优先级的SCHED_FIFO 或者SCHED_RR
    任务才能抢占SCHED_FIFO 任务。如果有两个或者更多的同优先级的SCHED_FIFO 级进程,它
    们会轮流执行,但是依然只有在它们愿意让出处理器时才会退出.只要有SCHED_FIFO 级进程
    在执行,其他级别较低的进程就只能等待它变为不可运行态后才有机会执行。
  2. SC阻止RR 级的进程在耗尽事先分配给它的
    时间后就不能再继续执行了.也就是说, SC阻止RR 是带有时闹片的SCHED_FIFO-这是一
    种实时轮流调度算挂. 当SCHED_RR 任务艳尽它的时间片时,在同一优先级的其他实时进程被
    轮流调度。时间片只用来重新调度同一优先级的进程。对于SCHED_FIFO 进程,高优先级总是
    立即抢占低优先级,但低优先级进程决不能抢占SCHED_RR 任务,即使它的时间片艳尽.

4.8调度相关的系统调用

  •  与调度策略和优先级相关的系统调用

    • sched _ setschedulerO 和sched__getscheduler()分别用于设置和获取进程的调度策略和实时优先
      级。与其他的系统调用相似,它们的实现也是囱许多参数检查、初始化和清理构成的。其实最重
      要的工作在于读取或改写进程tast_struct 的policy 和rt_priority 的值。
      sched _ setparam()和sched__getparam()分别用于设置和获取进程的实时优先级。这两个系统
      调用族取封装在sched_param 特殊结构体的rt_priority 中。sched__get_priority_max 0 和sched_
      get_priority _min()分别用于返回给定调度策略的最大和最小优先级。实时调度策略的最大优先级
      是MAX_ USER_RT_PRIO 减1,最小优先级等于1.
      对于一个普通的进程, nice()函数可以将给定进程的静态优先级增加一个给定的量。只有超
      级用户才能在调用它时使用负值,从而提高进程的优先级。nice()函数会调用内核的set_ user_
      nice()函数, 这个函数会设置进程的task_struct 的static_prio 和prio 值。
  • 与处理器绑定有关的系统调用
    • Linux 调度程序提供强制的处理器绑定(processor affinity?机制。也就是说,虱然它尽力通
      过一种软的〈或者说自然的)亲和性试图使进程尽量在同一个处理器上运行,但它也允许用户
      强制指定“这个进程无论如何都必须在这些处理器上运行”。这种强制的亲和性保存在进程task_
      struct 的cpus_allowed 这个位掩码标志中。该掩码标志的每一位对应一个系统可用的处理器。默
      认情况下,所有的位都被设置,进程可以在系统中所有可用的处理器上执行。用户可以通过
      sched _ setaffinity()设置不同的一个或几个位组合的位掩码,而调用scbed_ getaffinity()则返回当
      前的叩us_allowed 位掩码。
  • 放弃处理器时间
    • Linux 通过sched_yieldO 系统调用,提供了一种让进程显式地将处理器时间让给其他等待执
      行进程的机制。它是通过将进程从活动队列中( 因为进程正在执行,所以色肯定位于此队列当
      中〉移到过期队列中实现的。由此产生的效果不仅抢占了该进程并将其放入优先级队列的最后
      面,还将其放入过期队列中一一这样能确保在一段时间内它都不会再被执行了。由于实时进程
      不会过期,所以属于例外。它们只被移动到其优先级队列的最后面〈不会放到过期队列中〉。在
      Linux 的早期版本中, sched_yield()的语义有所不同, 进程只会被放置到优先级队列的末尾,放
      弃的时间往往不会太长。现在,应用程序甚至内核代码在调用sched_yield()前,应该仔细考虑是
      否真的希望放弃处理器时间.
      内核代码为了方便,可以直接调用yield(),先要确定给定进程确实处于可执行状态,然后再
      调用sched__yield() o 用户空间的应用程序直接使用sched__yield()系统调用就可以了。
时间: 2024-12-18 20:45:39

20135302魏静静——课本第4章学习笔记的相关文章

20135302魏静静——课本第17章学习笔记

第17章   模块与设备 设备类型:在所有 Unix 系统中为了统一普通设备的操作所采用的分类. 模块: Linux 内核中用于按需加载和卸载目标码的机制. 内核对象:内核数据结构中支持面向对象的简单操作,还支持维护对象之间的父子关系. sysfs :表示系统中设备树的一个文件系统. 1. 设备类型 三种类型 块设备 字符设备 网络设备 2. 模块 1. Hello,World 模块的所有初始化函数必须符合形式:int my _ init (void); 退出函数必须符合形式:void my_e

20135302魏静静——课本第3章学习笔记

第三章   进程管理 本章主要内容: 进程和线程 进程的任务结构 进程和线程的创建 进程的终止 1. 进程和线程 进程:进程就是处于执行期的程序,实际上,进程就是正在执行的程序代码的实时结果: 线程:执行线程,简称线程,是进程中活动的对象(每个线程拥有独立的程序计数器.进程栈.和一组进程寄存器),内核调度的对象是线程,不是进程 进程提供2种虚拟机制:虚拟处理器和虚拟内存 每个进程有独立的虚拟处理器和虚拟内存, 每个线程有独立的虚拟处理器,同一个进程内的线程有可能会共享虚拟内存. 内核中进程的信息

20135302魏静静——课本1-2章学习笔记

第一章 Linux内核简介 一.Unix Unix是一个强大.健壮和稳定的操作系统. Unix——支持抢占式多任务.多线程.虚拟内存.换页.动态链接和TCP/IP网络. 二.操作系统和内核简介 操作系统是指在整个系统中负责完成最基本功能和系统管理的那些部分. 内核独立于普通应用程序,一般处于系统态,拥有受保护的内存空间和访问硬件设备的所有权限.这种系统态和被保护起来的内存空间,统称为内核空间.在系统中运行的应用程序通过系统调用来与内核通信. 应用程序完成其工作的基本行为方式是: 应用程序通过系统

20135302魏静静——课本5章学习笔记

第五章 系统调用 5.1 与内核通信 中间层 作用三个:1.为用户空间提供一种硬件的抽象接口:2.保证系统稳定和安全:3.除异常和陷入,是内核唯一的合法入口. 5.2 API.POSIX和C库 API定义了应用程序使用的编程接口(可实现系统调用). API.POSIX.C库与系统调用之间关系. 5.3 系统调用——syscall 5.3.1 系统调用号 当用户空间的进程执行一个系统调用,就用系统调用号指明到底执行哪个系统调用. sys_ni_syscall():错误号,负责“填补空缺”,返回-E

20135302魏静静——课本18章学习笔记

第十八章 调试 一.内核中的bug 内核bug的原因可能有: - 错误代码 - 同步时发生的错误,例如共享变量锁定不当 - 错误的管理硬件 - …… 内核bug发作的症状可能有: - 降低所有程序的运行性能 - 毁坏数据 - 使得系统处于死锁状态 - …… 内核开发比起用户开发要多考虑一些独特的问题,比如: - 定时限制 - 竞争条件 - …… 原因是允许多个线程在内核中同时运行. 二.通过打印来调试 1. 健壮性 弹性极佳的函数:任何时候.任何地方都能调用它 可以在中断上下文和进程上下文中被调

20135302魏静静——Linux课程期中总结

Linux期中总结 Linux课程第一周实验及总结:[http://www.cnblogs.com/20135302wei/p/5218607.html] 冯诺依曼体系结构的核心思想是存储程序计算机.在计算机中有两种指令,一是用户指令,一是系统调用. 当用户使用计算机时,计算机根据其汇编的指令一步步运行,当使用系统调用完后,再返回用户模式,保证系统的稳定. 程序中执行call的堆栈变化 汇编基础 通用寄存器 16位  32位 AX    eax 累加器BX    ebx 基址寄存器CX    e

【tapestry3笔记】--tapestry 初探,《 tapestry in action 》第一章学习笔记

由于要维护一个项目,要用到tapestry3这个老框架,虽然这个框架很老,但是在我看来ta的思想还是很先进的---面向组件编程. 由于网上资料少的可怜,辛苦找了很久终于找到一本名为<tapestry in action>的工具书,以下学习笔记均以此书为参考. 正文---tapestry初探 tapestry in action 第一章学习笔记 tapestry是一款以组件为核心的开发框架,组件就向一个黑盒子,我们无需关系组件是如何实现的,只需合理使用即可.这有点像jquery的插件,我们无需关

《Linux内核设计与实现》第一、二章学习笔记

<Linux内核设计与实现>第一.二章学习笔记 姓名:王玮怡  学号:20135116 第一章 Linux内核简介 一.关于Unix ——一个支持抢占式多任务.多线程.虚拟内存.换页.动态链接和TCP/IP网络的现代化操作系统 1.主要发展过程   1969年,贝尔实验室的程序员们设计了一个文件系统原型,最终发展演化成了Unix 1971年,Unix被移植到PDP-11型机中 1973年,整个Unix系统使用C语言进行重写,为后来Unix系统的广泛移植铺平了道路 Unix第六版(V6)被贝尔实

安卓权威编程指南 - 第五章学习笔记(两个Activity)

学习安卓编程权威指南第五章的时候自己写了个简单的Demo来加深理解两个Activity互相传递数据的问题,然后将自己的学习笔记贴上来,如有错误还请指正. IntentActivityDemo学习笔记 题目:ActivityA登录界面(用户名.密码.登陆按钮),ActivityB(Edit,返回按键:SubmitButton).A界面输入用户名和密码传到B中,B验证用户输入的用户名和密码,如果错误就返回A,并用Toast 显示用户名和密码错误:如果正确,就在第二个 activity中显示一个Edi