进程调度(四)

简介: 版权声明:您好,转载请留下本人博客的地址,谢谢 https://blog.csdn.net/hongbochen1223/article/details/46603765 紧接上一篇...
版权声明:您好,转载请留下本人博客的地址,谢谢 https://blog.csdn.net/hongbochen1223/article/details/46603765

紧接上一篇!!

(二)抢占和进程上下文

上下文切换,就是从一个可执行进程切换到另一个可执行进程,由定义在kernel/sched.c中的context_switch()函数处理,该函数主要完成两项基本工作:

​1:调用声明在asm/mmu_context.h中的switch_mm(),该函数负责把虚拟内存从上一个进程映射切换到新进程中。
​2:调用声明在asm/system.h文件中的switch_to()函数,该函数负责从上一个进程的处理器状态切换到新进程的处理器状态。这包括保存,恢复栈信息和寄存器信息,还有其他任何与体系结构相关的信息,都必须以每一个进程为对象进行管理和保存。

下面我们看一下函数context_switch()的代码:

/*
 * context_switch - switch to the new MM and the new
 * thread's register state.
 *
 * context_switch - 切换到一个新的MM(内存)和新的进程
 * 的寄存器状态
 */
static inline void
context_switch(struct rq *rq, struct task_struct *prev,
           struct task_struct *next)
{
    struct mm_struct *mm, *oldmm;
    prepare_task_switch(rq, prev, next);
    trace_sched_switch(rq, prev, next);
    mm = next->mm;
    oldmm = prev->active_mm;
    /*
     * For paravirt, this is coupled with an exit in switch_to to
     * combine the page table reload and the switch backend into
     * one hypercall.
     */
    arch_start_context_switch(prev);
    if (likely(!mm)) {
        next->active_mm = oldmm;
        atomic_inc(&oldmm->mm_count);
        enter_lazy_tlb(oldmm, next);
    } else
        switch_mm(oldmm, mm, next);
    if (likely(!prev->mm)) {
        prev->active_mm = NULL;
        rq->prev_mm = oldmm;
    }
    /*
     * Since the runqueue lock will be released by the next
     * task (which is an invalid locking op but in the case
     * of the scheduler it's an obvious special-case), so we
     * do an early lockdep release here:
     */
#ifndef __ARCH_WANT_UNLOCKED_CTXSW
    spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
#endif
    /* Here we just switch the register state and the stack. */
    switch_to(prev, next, prev); //切换处理器状态,并保存上一个进程的处理器状态
    barrier();
    /*
     * this_rq must be evaluated again because prev may have moved
     * CPUs since it called schedule(), thus the 'rq' on its stack
     * frame will be invalid.
     */
    finish_task_switch(this_rq(), prev);
}

下面我们来整体的看一下休眠和唤醒的函数调用和过程:

这里写图片描述

内核提供need_resched变量来表明函数是否需要重新进行一次调度,下面是用于访问和操作need_resched变量的函数。

这里写图片描述

在返回用户空间以及从中断返回的时候,内核也会检查need_reched标志,如果已被设置,内核会在继续执行之前调用调度程序。

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

​1:用户抢占
内核即将返回用户空间的时候,如果need_resched标志被设置,会导致schedule()被调用,此时就会发生用户抢占。

用户抢占在一下情况下发生:
​1:从系统调用返回用户空间的时候
​2:从中断处理函数返回用户空间的时候

2:内核抢占

Linux完整的支持内核抢占,但是,由于内核抢占会出现一些安全性问题,那么,内核抢占时机的确定是相当重要的,那么什么时候进行重新调度是安全的呢?只要没有锁,内核进可以进行抢占。

Linux为了支持内核抢占,在thread_info中引入了preempt_count计数器。该计数器的初始值为0,每当使用锁的时候,该值加1,当释放锁的时候,该值减1,当该数值为0的时候,内核就可以抢占。当从中断返回内核空间的时候,内核就会检查need_resched标志和preempt_count计数器。如果need_resched标志被设置,并且preempt_count数值为0,调度程序就会被执行。

如果此时的preempt_count不为0,说明当前任务持有锁,所以抢占是不安全的。 这时,内核就会像通常那样直接从中断返回当前执行进程。如果当前执行进程持有的所有的锁都被释放了,preempt_count数值为0,此时,释放锁的代码就会检查need_resched标志是否被设置,如果被设置的话,就会调用调度程序。

内核抢占发生的时间:

​1:中断处理程序正在执行,且返回内核空间之前
​2:内核代码再一次具有可抢占性的时候
​3:如果内核中的任务显示的调用schedule()函数
​4:如果内核中的任务阻塞                                   

(二):实时调度策略
Linux提供了两种实时调度策略:SCHED_FIFO和SCHED_RR,而普通的,非实时的调度策略是SCHED_NORMAL。这些实时调度策略被是个特殊的实时调度器管理,定义在kernel/sched_rt.c中。

​1:SCHED_FIFO

​这是一个先入先出的调度算法,他不使用时间片。处于可运行状态的SCHED_FIFO级进程会比任何SCHED_NORMAL进程都先得到调度。一旦一个SCHED_FIFO进程处于可执行状态,就会一直执行下去,只有更高优先级的SCHED_FIFO和SCHED_RR任务才能抢占SCHED_FIFO任务。如果有两个或多个同优先级的SCHED_FIFO级进程,他们会轮流执行,但是依然只有他们愿意让出处理器的时候才会退出。只要有SCHED_FIFO级进程在执行,其他级别较低的进程就只能等待他变为不可运行状态后才有机会执行。

​2:SCHED_RR

SCHED_RR和SCHED_FIFO大体相同,只是 SCHED_RR级的进程在耗尽事先分配给他的时间后就不在继续执行了,也就是说SCHED_RR是带有时间片的SCHED_FIFO,这是一种实时轮流调度算法。当SCHED_RR任务耗尽他的时间片的时候,在同一优先级的其他实时进程被轮流调度。时间片只用来重新调度同一优先级的进程。对于SCHED_FIFO进程,高优先级进程总是立即抢占低优先级,但是低优先级决不能抢占SCHED_RR任务,即使他的时间片耗尽。

Linux的实时调度算法提供了一种软实时工作方式。软实时的含义是,内核调度进程,尽力使进程在他的限定时间到来之前运行,但内核不保证总能满足这些进程的要求。

实时优先级范围是从0到MAX_RT_PRIO-1。默认情况下,MAX_RT_PRIO为100,所以默认的实时优先级的范围是0-99.SCHED_NORMAL级进程的nice值共享了这个取值空间,他的取值范围是从MAX_RT_PRIO到(MAX_RT_PRIO+40)。也就是说,在默认情况下,nice值从-20到+19直接对应的是从100-139的实时优先级范围。

(三):与调度相关的系统调用
Linux提供了一个系统调用族,用于管理与调度程序相关的参数。下面我们来看一下系统调用。

这里写图片描述

Linux调度程序提供强制的处理器绑定(processor affinity)机制。也就是说,他允许用户强制指定”这个进程必须在这些处理器上执行“。这种强制的亲和性保存在进程的task_struct结构中的cpus_allowed这个位掩码标志中。该掩码标志的每一位对应一个系统可用的处理器。默认情况下,所有的位都被设置,进程可以在系统中所有可用的处理器上执行。用户可以通过sched_setaffinity()设置不同的一个或几个位组合的掩码,而调用sched_getaffinity()则返回当前的cpus_allowed位掩码。

Linux通过sched_yield()系统调用,提供了一种让进程显示地将处理器时间让给其他等待执行进程的机制。

目录
相关文章
|
4天前
|
算法 调度 UED
深入理解操作系统:进程调度与优先级队列
【10月更文挑战第31天】在计算机科学的广阔天地中,操作系统扮演着枢纽的角色,它不仅管理着硬件资源,还为应用程序提供了运行的环境。本文将深入浅出地探讨操作系统的核心概念之一——进程调度,以及如何通过优先级队列来优化资源分配。我们将从基础理论出发,逐步过渡到实际应用,最终以代码示例巩固知识点,旨在为读者揭开操作系统高效管理的神秘面纱。
|
1天前
|
算法 调度 UED
深入理解操作系统:进程管理与调度策略
【10月更文挑战第34天】本文旨在探讨操作系统中至关重要的一环——进程管理及其调度策略。我们将从基础概念入手,逐步揭示进程的生命周期、状态转换以及调度算法的核心原理。文章将通过浅显易懂的语言和具体实例,引导读者理解操作系统如何高效地管理和调度进程,保证系统资源的合理分配和利用。无论你是初学者还是有一定经验的开发者,这篇文章都能为你提供新的视角和深入的理解。
12 3
|
5天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
30 4
|
6天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
6天前
|
消息中间件 算法 调度
深入理解操作系统:进程管理与调度策略
【10月更文挑战第29天】本文将带领读者深入探讨操作系统中的核心组件之一——进程,并分析进程管理的重要性。我们将从进程的生命周期入手,逐步揭示进程状态转换、进程调度算法以及优先级调度等关键概念。通过理论讲解与代码演示相结合的方式,本文旨在为读者提供对进程调度机制的全面理解,从而帮助读者更好地掌握操作系统的精髓。
19 1
|
6天前
|
算法 调度 UED
深入理解操作系统中的进程调度
【10月更文挑战第29天】探索进程调度的奥秘,本文将带你深入了解在操作系统中如何管理和控制多个并发执行的程序。从简单的调度算法到复杂的多级反馈队列,我们将逐步揭示如何优化系统性能和提高资源利用率。准备好一起揭开进程调度的神秘面纱吧!
|
11天前
|
算法 大数据 Linux
深入理解操作系统之进程调度算法
【10月更文挑战第24天】本文旨在通过浅显易懂的语言,带领读者深入了解操作系统中的进程调度算法。我们将从进程的基本概念出发,逐步解析进程调度的目的、重要性以及常见的几种调度算法。文章将通过比喻和实例,使复杂的技术内容变得生动有趣,帮助读者建立对操作系统进程调度机制的清晰认识。最后,我们还将探讨这些调度算法在现代操作系统中的应用和发展趋势。
|
1月前
|
算法 调度
深入理解操作系统:进程调度与优先级反转问题
【9月更文挑战第36天】操作系统是计算机科学中的核心概念,它管理着计算机的硬件资源和软件进程。在多任务处理环境中,进程调度是保证系统高效运行的关键机制之一。本文将探讨进程调度的基本概念、调度算法以及它们如何影响系统性能。同时,我们还将讨论优先级反转问题,这是一个在实时系统中常见的问题,它可能导致系统响应时间不可预测。通过分析优先级反转的原因和解决方案,我们可以更好地理解操作系统的设计和优化策略。
|
28天前
|
算法 调度 UED
探索操作系统的心脏:深入理解进程调度
【10月更文挑战第7天】在数字世界的海洋中,操作系统是那艘承载着软件与硬件和谐共处的巨轮。本文将带你潜入这艘巨轮的核心区域——进程调度系统,揭示它如何精准控制任务的执行顺序,保障系统的高效运行。通过深入浅出的语言,我们将一起解码进程调度的奥秘,并借助代码示例,直观感受这一机制的魅力所在。准备好,让我们启航吧!
|
4天前
|
算法 Linux 调度
深入理解操作系统之进程调度
【10月更文挑战第31天】在操作系统的心脏跳动中,进程调度扮演着关键角色。本文将深入浅出地探讨进程调度的机制和策略,通过比喻和实例让读者轻松理解这一复杂主题。我们将一起探索不同类型的调度算法,并了解它们如何影响系统性能和用户体验。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇理解操作系统深层工作机制的大门。
10 0

热门文章

最新文章

相关实验场景

更多