百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

深入理解Linux下的进程调度

sinye56 2024-12-13 16:06 4 浏览 0 评论

前言;长期以来,Linux一直把具有较好的平均系统响应时间和较高的吞吐量作为调度算法的主要目标。但近年来,鉴于嵌入式系统的要求,Linux2.6在支持系统的实时性方面也做出了重大的改进。

一,Linux进程的时间片与权重参数

在处理器资源有限的系统中,所有进程都以轮流占用处理器的方式交叉运行。为使每个进程都有运行的机会,调度器为每个进程分配了一个占用处理器的时间额度,这个额度叫做进程的“时间片”,其初值就存放在进程控制块的counter域中。进程每占用处理器一次,系统就将这次所占用时间从counter中扣除,因为counter反映了进程时间片的剩余情况,所以叫做剩余时间片。

Linux调度的主要思想为:调度器大致以所有进程时间片的总和为一个调度周期;在每个调度周期内可以发生若干次调度,每次调度时,所有进程都以counter为资本竞争处理器控制权,counter值大者胜出,优先运行;凡是已耗尽时间片(即counter=0)的,则立即退出本周期的竞争;当所有未被阻塞进程的时间片都耗尽,那就不等了。然后,由调度器重新为进程分配时间片,开始下一个调度周期。

Linux基于时间片调度的基本思想如下图所示:

上面的调度方式主要体现的是一种公平性:如果一个进程的剩余时间片多,那么它在前期获得运行的时间片就少,为了公平性,就应该适当的赋予它更高的优先级。但是这仅仅是一个总体的原则,为了应付实际问题中的特殊状况,在上述平等原则的基础上,为了给特殊进程一些特殊照顾,在为进程分配时间片时,Linux允许用户通过一个系统调用nice()来设置参数nice影响进程的优先权。所以,系统真正用来确定进程的优先权时,使用的依据为权重参数weight,weight大的进程优先运行。

weight与nice、counter之间的关系大体如下:

weight 正比于 [counter+(20-nice)]

关于三者之间的这个关系将会在后面的内容中详细地讲到。

因为nice是用户在创建进程时确定的,在进程的运行过程中一般不会改变,所以叫做静态优先级;counter则随着进程时间片的小号在不断减小,是变化的,所以叫做动态优先级。

调度策略

目前,标准Linux系统支持非实时(普通)和实时两种进程。与此相对应的,Linux有两种进程调度策略:普通进程调度和实时进程调度。因此,在每个进程的进程控制块中都有一个域policy,用来指明该进程为何种进程,应该使用何种调度策略。

Linux调度的总体思想是:实时进程优先于普通进程,实时进程以进程的紧急程度为优先顺序,并为实时进程赋予固定的优先级;普通进程则以保证所有进程能平均占用处理器的时间为原则。所以其具体做法就是:对于实时进程来说,总的思想是为实时进程赋予远大于普通进程的固定权重参数weight,以确保实时进程的优先级。在此基础上,还分为两种做法:一种与时间片无关,另一种与时间片有关;对于普通进程来说,原则上以相等的weight作为所有进程的初始权重值,即nice=0,然后在每次进行进程调度时,根据剩余时间片对weight动态调整。

后台私信【内核大礼包】,内核大礼包资料(里面包含视频教程、电子书、实战项目及代码)。

普通进程调度策略

如果进程控制块的policy的值为SCHED_OTHER,则该进程为普通进程,适用于普通进程调度策略。

时间片的分配

当一个普通进程被创建时,系统会先为它分配一个默认的时间片(nice=0)。在Linux2.4内核中,进程的默认时间片时按照下面的算法来计算的:

#if HZ < 200
#define TICK_SCALE(x)        ((x)>>2)
#elif HZ < 400
#define TICK_SCALE(x)        ((x)>>1)
#elif HZ < 800
#define TICK_SCALE(x)        (x)
#elif HZ < 1600
#define TICK_SCALE(x)        ((x)<<1)
#dele
#define TICK_SCALE(x)        ((x)<<2)
#endif
#define NICE_TO_TICKS(nice) (TICK_SCALE(20-(nice))+1)
......

在每个调度周期之前,调度器为每个普通进程进程分配时间片的算法为:

p->counter = (p->counter>>1) + NICE_TO_TICKS(p->nice);

其中,p为进程控制块指针。这里为什么需要加上p->counter>>1呢?这会在本文后面的内容中讲到。例如,用户定义HZ为100,而counter初值和nice的默认值为0,于是可以计算出counter的值为6 ticks(((20-0)>>2)+1),也就是说,进程时间片的默认值大概为60ms(100Hz,10ms)。

weight的微调函数goodness()

尽管在默认情况下,系统为所有的进程都分配了相等的时间片,但在实际运行时,常常因各种原因使得进程的运行总是有先有后,于是经过一段时间运行后,在各进程之间就会产生事实上的不公平的现象,也就是各个进程在实际使用其时间片的方面形成了差异。剩余时间计数器counter的值就反映了这个差异的程度:该值大的,意味着这个进程占用处理器的时间少(吃亏了);该值小的,意味着这个进程占用处理器的时间多(占便宜了)。

为了尽可能地缩小上述的这个差异,Linux的调度器在调度周期中每次调度时,都遍历就绪列表中的所有进程,并按照各个进程的counter当前值调用函数goodness()对所有进程的weight进行调整,想办法让counter值大的进程的weight大一些,而counter值小的进程的weight小一些。

函数goodness()的主要代码如下:

/*
返回值:
        -1000:从不选择这个
        0:过期进程,重新计算计数值(但它仍旧可能被选中)
        正值:goodness值(越大越好)
        +1000:实时进程,选择这个
*/
 
static inline int goodness(struct task_struct * p, int this_cpu, struct mm_struct *this_mm)
{
     int weight;
     weight = -1;
 
     if (p->policy & SCHED_YIELD) //p->policy表示进程的调度策略,SCHED_YIELD是一种策略,不参与处理器的竞争!
#define SCHED_YIELD          0x10
          goto out;
 
     //非实时进程
     if (p->policy == SCHED_OTHER) {
          weight = p->counter;                //weight的主要成分是counter
          if (!weight)                        //如果时间片用尽
               goto out;
              
#ifdef CONFIG_SMP
          if (p->processor == this_cpu)
               weight += PROC_CHANGE_PENALTY;
#endif
 
          if (p->mm == this_mm || !p->mm)
               weight += 1;
          weight += 20 - p->nice;            //nice越小,权值越大
          goto out;
     }
 
     //实时进程
     weight = 1000 + p->rt_priority;
out:
     return weight;
}

这个函数中就能看出:weight 正比于 [counter+(20-nice)]。

普通进程调度算法中的一些细节

当普通就绪队列中所有非等待进程counter值都减为0时,就在schedule()中对每个进程利用下面的代码:

p->counter = (p->counter>>1) + NICE_TO_TICKS(p->nice);

对所有的进程时间片counter重新赋值。在重新赋值时,对于耗尽时间片的进程,由于参与计算的counter等于0,因此这个算法就是恢复counter的初值;而对于处于等待状态的进程,由于参与计算的counter大于0,因此这个算法实际上就是把counter的剩余值折半再加上初值。也就是说,因等待而损失了时间片的进程在counter重新被赋值时,系统会适当地给它一些“照顾”,以使其在下一个调度周期中获得更多的时间片,并拥有更高的权重weight。

一个进程的等待次数比较多,通常意味着它的I/O操作比较密集。通过对时间片进行叠加的做法给等待进程赋予更高的优先级,体现了Linux对I/O操作密集型进程的一种体贴。

可见,SCHED_OTHER策略本质上是一种比例共享的调度策略,它的这种设计方法能够保证进程调度时的公平性:一个低优先级的进程在每一个周期中也会得到自己应得的那些运行时间;同时,它提供了nice使用户可以对进程优先级进行干预。

实时进程调度策略

凡是进程控制块的policy的值为SCHED_FIFO或SCHED_RR的进程,调度器都将其视为实时进程。

进程控制块中的域rt_pripority就是实时进程的优先级,其范围是1~99。这一属性将在调度时用于对进程权重参数weight的计算。

从上面介绍的函数goodness()代码中可以看到,计算实时进程weight的代码相当简单,即:

weight = 1000 + p->rt_priority;

也就是说,Linux是按照严格的优先级别来选择待运行进程的,并且实时进程的权重weight远高于普通进程。

普通进程参数counter、nice,实时进程参数rt_pripority,调度器调度依据的参数weight之间的关系如下图所示:

Linux允许多个实时进程具有相同的一个优先级别,Linux把所有的实时进程按照其优先级组织成若干个队列,对于同一队列的实时进程可以采用先来先服务和时间片轮转调度两种调度策略。

先来先服务调度(SCHED_FIFO)

该策略是符合POSIX标准的FIFO(先入先出)调度规则。即在同一级别的队列中,先就绪的进程先入队,后就绪的进程后入队。调度器在选择运行进程时,就以优先级rt_pripority为序查询各个队列,当发现队列中有就绪进程时,就运行处于队列头位置的进程。其后,它就会一直运行,除非出现下述情况之一:

  • 进程被具有更高优先级别进程剥夺了处理器;
  • 进程自己因为请求资源而堵塞;
  • 进程自己主动放弃处理器。

时间片轮转调度(SCHED_RR)

该策略是符合POSIX标准的RR(循环Round-Robin)调度规则。

这种策略与SCHED_FIFI类似,也根据优先级别把就绪进程分成若干个队列,但每个队列被组织成一个循环队列,并且每个进程都拥有一个时间片。调度时,调度器仍然以优先级别为序查询就绪进程队列。当发现就绪进程队列后,也是运行处在队列头部的进程,但是当这个进程将自己的时间片耗完之后,调度器把这个进程放到其队列的队尾,并且再选择处在队头的进程来运行,当然前提条件是这时没有更高优先级别的实时进程就绪。

二,Linux调度时机

进程调度的时机与引起进程调度的原因和进程调度的方式有关。Linux进程调度的时机主要有:

  • 进程状态转换的时刻,如进程中止、进程睡眠等;
  • 可运行队列中新增加一个进程时;
  • 当前进程的时间片用完时;
  • 进程从系统调用返回用户态时;
  • 内核处理完中断后,进程返回用户态时。

但必须注意当前进程控制块中的域need_resched的值为非0时,才允许发生调度。另外,要注意,不能在中断服务程序中调用调度器进行调度。

相关推荐

linux 查看当前应用内存状况,以及内存参数含义

1、查看进程号ps-ef|greptomcat2、查看当前内存分配,200ms打印一次jstat-gc进程号2001jstat-gc344802001S0CS1C...

如何显示 Linux 系统上的可用内存?这几个命令很好用!

在Linux系统中,了解可用内存是优化系统性能、故障排查以及资源管理的重要一环。本文将详细介绍如何在Linux系统上显示可用内存,包括多种方法和工具的使用。在讨论可用内存之前,我们需要了解一些...

Linux 下查看内存使用情况方法总结

Q:我想监视Linux系统的内存使用情况,在Linux下有哪些视图或者命令行工具可用呢?在做Linux系统优化的时候,物理内存是其中最重要的一方面。自然的,Linux也提供了非常多的方法来监控宝贵的内...

2、linux命令-用户管理

linux命令-用户管理用户切换[root@eric~]#sueric#切换到用户eric[eric@ericroot]$[eric@ericroot]$su#切换到rootPas...

Centos 7 进入单用户模式详解

1、开机在启动菜单按e进入编辑模式找到linux16行,在最后添加init=/bin/sh编辑完后,按ctrl+x退出2、进单用户模式后,使用passwd修改密码,提示以下错误:passwd:Aut...

每日一个Linux命令解析——newusers

newusers:在Linux系统中,newusers是一个用于批量创建用户的命令。它从一个文件中读取多行用户信息,每行描述一个用户的详细信息,并根据这些信息创建多个用户或对现有用户进行批量修改。一...

openEuler操作系统管理员指南:管理用户与用户组

在Linux中,每个普通用户都有一个账户,包括用户名、密码和主目录等信息。除此之外,还有一些系统本身创建的特殊用户,它们具有特殊的意义,其中最重要的是管理员账户,默认用户名是root。同时Linux也...

Linux用户管理

1、用户信息文件/etc/passwdroot:x:0:0:root:/root:/bin/bash第一列:用户名第二列:密码位第三列:用户ID0超级用户UID。如果用户UID...

centos7基础-用户、组、权限管理

用户和组(1)用户、组、家目录的概念linux系统支持多用户,除了管理员,其他用户一般不应该使用root,而是应该向管理员申请一个账号。组类似于角色,系统可以通过组对有共性的用户进行统一管理。每个用户...

LINUX基础 ----------组及用户的概念

在Linux中,用户和组都是非常重要的概念,可以控制文件访问权限和资源的管理。用户是标识一个进程、应用程序或系统管理员的账号,Linux中每个用户用一个用户ID(UID)来标识。对于一个...

从零入门Linux(四)用户与权限管理

在Linux系统中,用户和权限管理是系统安全的重要组成部分。通过合理配置用户和权限,可以确保系统的安全性和资源的合理分配。以下是一些与用户和权限管理相关的常用命令和概念。1.用户管理1.1添加...

如何在 Linux 中管理用户?

在Linux系统中,用户是系统资源的主要使用者,每个用户都有一个唯一的标识符(用户ID)。为了更好地组织和管理用户,Linux还引入了用户组的概念。用户组是用户的集合,有助于更有效地分配权限和资...

在 Linux 中将用户添加到特定组的四种方法

在Linux多用户操作系统中,用户组管理是系统安全架构的基石。通过合理的组权限分配,管理员可以实现:精确控制文件访问权限(chmod775project/)简化批量用户权限管理(setfacl-...

我不是网管 - 如何在Ubuntu Linux下创建sudo用户

Sudo用户是Linux系统的普通用户,具有一定的管理权限,可以对系统执行管理任务。在Linux中,root是超级用户,拥有完全的管理权限,但不建议将root凭证授予其他用户或作为r...

Linux创建普通用户,为密钥方式登录做准备

Hi,我是聪慧苹果8,就是江湖上人见人爱、花见花开,土到掉榨的Linux爱好者,一起学习吧!上一篇关于SSH安全加固的文字,有网友点评通过密钥登录更加安全,先创建一个普通用户,拒绝直接使用密码登录,这...

取消回复欢迎 发表评论: