【Linux】进程调度概述

1 可执行队列

(基于实时进程调度)

调度程序中最基本的数据结构式运行队列(runqueue)。可执行队列是给定处理器上的可执行进程的链表,每个处理器一个。每个可投入运行的进程都唯一的归属于一个可执行队列。此外,可执行队列中还包含每个处理器的调度信息。所以,可执行队列也是每个处理器最重要的数据结构。

为了避免死锁,要锁住多个运行队列的代码必须总是按照同样的顺序获取这些锁:按照可执行队列地址从低向高的顺序。

注:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

runqueue是一个双向循环队列,一旦调度时机触发,内核重新计算当前队列中所有进程运行权值,并从中挑选出权值最高的进程作为当前进程投入运行。其弊端:

1)调度时机触发,重新计算runqueue中每个进程运行权值,复杂度为O(n), 且调度性能与内核负载相关。

2)runqueue同时管理着实时进程与非实时进程(普通进程),内核通过进程属性,如实时或非实时、实时进程优先级、用户进程或内核线程相关因素来计算运行权值count,灵活性低,且不便于理解和维护。

2 优先级数组

每个运行队列都有两个优先级数组,一个活跃的和一个过期的,是prio_array类型的结构体。优先级数组时一种能够提供O(1)级算法发杂度的数据结构。优先级数组使可运行处理器的每一种优先级都包含一个相应的队列,而这些队列包含对应优先级上的可执行进程链表。

每个优先级数组还包含一个叫做struct list_head的队列,每个链表与一个给定的优先级相对应,每个链表都包含该处理器队列上相应优先级的全部可运行进程。

3 重新计算时间片

在所有的进程的时间片(CPU分配给各个程序的时间)都用完时,都采用一种显式的方法来重新计算每个进程的时间片。

新的Linux调度程序减少了对循环的依赖。取而代之的是它为每个处理器维护两个优先级数组:既有活动数组也有过期数组。活动数组内的可执行队列上的进程都还有时间片剩余;而过期数组内的可执行队列上的进程都耗尽了时间片。当一个进程的时间片耗尽时,它会被移至过期数组,但在此之前,时间片已经给它重新计算好了。

4 schedule()

选定下一个进程并切换到它去执行是通过schedule()函数实现的。当内核代码想要休眠时,会直接调用该函数,另外,如果有哪个进程将被抢占,那么该函数也会被唤起执行。schedule()函数独立于每个处理器运行。

5 计算优先级和时间片

nice值之所以起名为静态优先级,是因为它从一开始由用户指定后,就不能改变。动态优先级通过一个关于静态优先级和进程交互性的函数关系计算而来。effective_prio()函数可以返回一个进程的动态优先级。这个函数以nice值为基数,再加上-5到+5之间的进程交互性的奖励或罚分。

通过一些推断来获取准确反映进程到底是I/O消耗型的还是处理器消耗型的。最明显的标准莫过于进程休眠的时间长短了。如果一个进程的大部分时间都在休眠,那么它就是I/O消耗型的。如果一个进程执行的时间比休眠的时间长,那它就是处理器消耗型的。

另一方面,重新计算时间片相对简单了。它只要以静态优先级为基础就可以了。在一个进程创建的时候,新建的子进程和父进程均分父进程剩余的进程时间片。这样的分配很公平并且防止用户通过不断创建新进程来不停地获取时间片。task_timeslice()函数为给定任务返回一个新的时间片。时间片的计算只需要把优先级按比例缩放,使其符合时间片的数值范围要求就可以了。

调度程序还提供了另外一种机制以支持交互进程:如果一个进程的交互性非常强,那么当它时间片用完后,它会被放置到活动数组而不是过期数组中。

6 睡眠和唤醒

休眠(被阻塞)的进程处于一个特殊的不可执行状态。进程把它自己标记成休眠状态,把自己从可执行队列移出,放入等待队列,然后调用schedule()选择和执行一个其他进程。唤醒的过程刚好相反:进程被设置为可执行状态,然后再从等待队列中移到可执行队列。

休眠有两种相关的进程状态:TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE。休眠通过等待队列进行处理。等待队列是由等待某些事件发生的进程组成的简单链表。内核用wake_queue_head_t来代表等待队列。等待队列可以通过DECLARE_WAITQUEUE()静态创建,也可以由init_waitqueue_head()动态创建。唤醒操作通过函数wake_up()进行,它会唤醒指定的等待队列上的所有进程。关于休眠有一点需要注意,存在虚假的唤醒。有时候进程唤醒并不是因为它所等待的条件达成了,所以才需要用一个循环处理来保证它等待的条件真正达成。

7 负载平衡程序

负载平衡程序有kernel/sched.c中的函数load_balance()来实现。它有两种调用方法。在schedule()执行的时候,只要当前的可执行队列为空,它就会被调用。此外,它还会被定时器调用:系统空闲时每隔1毫秒调用一次或者在其他情况下每隔200毫秒调用一次。负载平衡程序调用时需要锁住当前处理器的可执行队列并且屏蔽中断,以避免可执行队列被并发地访问。

时间: 2024-10-29 10:46:20

【Linux】进程调度概述的相关文章

Linux 进程调度浅析

概述 操作系统要实现多进程,进程调度必不可少.有人说,进程调度是操作系统中最为重要的一个部分.我觉得这种说法说得太绝对了一点,就像很多人动辄就说"某某函数比某某函数效率高XX倍"一样,脱离了实际环境,这些结论是比较片面的. 而进程调度究竟有多重要呢? 首先,我们需要明确一点:进程调度是对 TASK_RUNNING 状态的进程进行调度.如果进程不可执行(正在睡眠或其他),那么它跟进程调度没多大关系.所以,如果你的系统负载非常低,盼星星盼月亮才出现一个可执行状态的进程.那么进程调度也就不会

【原创】(六)Linux进程调度-实时调度器

背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本:4.14 ARM64处理器,Contex-A53,双核 使用工具:Source Insight 3.5, Visio 1. 概述 在Linux内核中,实时进程总是比普通进程的优先级要高,实时进程的调度是由Real Time Scheduler(RT调度器)来管理,而普通进程由CFS调度器来管理. 实

linux进程调度机制剖析(基于3.16-rc4)

进程调度所使用到的数据结构: 1.就绪队列 内核为每一个cpu创建一个进程就绪队列,该队列上的进程均由该cpu执行,代码如下(kernel/sched/core.c). 1 DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues); 定义了一个struct rq结构体数组,每个数组元素是一个就绪队列,对应一个cpu.下面看下struct rq结构体(kernel/sched/sched.h): 1 struct rq { 2 /* runqueue

Linux进程调度(3):进程切换分析

3.调度函数schedule()分析 当kernel/sched.c:sched_tick()执行完,并且时钟中断返回时,就会调用kernel/sched.c:schedule()完成进程切换.我们也可以显示调用schedule(),例如在前面"Linux进程管理"的介绍中,进程销毁的do_exit()最后就直接调用schedule(),以切换到下一个进程. schedule()是内核和其他部分用于调用进程调度器的入口,选择哪个进程可以运行,何时将其投入运行.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学习笔记之三————Linux命令概述

一.引言 很多人可能在电视或电影中看到过类似的场景,黑客面对一个黑色的屏幕,上面飘着密密麻麻的字符,梆梆一顿敲,就完成了窃取资料的任务. Linux 刚出世时没有什么图形界面,所有的操作全靠命令完成,就如同电视里的黑客那样,充满了神秘与晦涩. 近几年来,尽管 Linux 发展得非常迅速,图形界面越来越友好,但是在真正的开发过程中,Linux 命令行的应用还是占有非常重要的席位,而且许多Linux功能在命令行界面要比图形化界面下运行的快.可以说不会命令行,就不算会 Linux. Linux 提供了

Linux进程调度器概述--Linux进程的管理与调度(十五)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换. 什么是调度器 通常来说,操作系统是应用程序和可用资源之间的媒介. 典型

Linux进程调度器的设计--Linux进程的管理与调度(十七)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 前景回顾 进程调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换. 内核必须提供一种方法, 在各个进程之间尽可能公平地

Linux内核分析:实验八--Linux进程调度与切换

刘畅 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 这篇文章主要分析Linux中,进程调度和上下文切换的过程,会涉及到进度调度的时机和进程的切换执行过程,并通过GDB跟踪Linux的schedule()函数来比较深入的理解一下这个过程. 进程调度策略与调度时机 调度策略 操作系统中包含有很多进程调度的算法,内核会依据进程的属性来选择不同的调度策略.进程可以比较粗略的分为:IO密集