首页 > 系统相关 >linux 进程的管理和调度 --- __schedule() 函数分析

linux 进程的管理和调度 --- __schedule() 函数分析

时间:2023-11-05 20:58:08浏览次数:55  
标签:__ rq schedule mm next --- switch active prev

运行队列

Linux采用的是每个CPU都有自己的运行队列,这样做的好处:
(1)每个CPU在自己的运行队列上选择任务降低了竞争;
(2)某个任务位于一个CPU的运行队列上,经过多次调度后,内核趋于选择相同的CPU执行该任务,那么上次任务运行的变量很可能仍然在这个CPU缓存上,提高运行效率。

 __schedule()

 入参

#define SM_NONE            0x0  // 主动调度
#define SM_PREEMPT        0x1
#define SM_RTLOCK_WAIT        0x2

 函数分析

static void __sched notrace __schedule(unsigned int sched_mode)
{
    struct task_struct *prev, *next;
    unsigned long *switch_count;
    unsigned long prev_state;
    struct rq_flags rf;
    struct rq *rq;
    int cpu;

    cpu = smp_processor_id();
    rq = cpu_rq(cpu); // 找到当前CPU的运行队列
    prev = rq->curr; // 记录要切换出去的任务

    schedule_debug(prev, !!sched_mode);

    if (sched_feat(HRTICK) || sched_feat(HRTICK_DL))
        hrtick_clear(rq);

    local_irq_disable();
    rcu_note_context_switch(!!sched_mode);

    rq_lock(rq, &rf);
    smp_mb__after_spinlock();

    /* Promote REQ to ACT */
    rq->clock_update_flags <<= 1;
    update_rq_clock(rq);

    switch_count = &prev->nivcsw; // Context involuntary switch counter,此任务上下文非主动切换计数

    prev_state = READ_ONCE(prev->__state);
    if (!(sched_mode & SM_MASK_PREEMPT) && prev_state) { // 主动调度,并且要切换出去的任务不是运行态(非运行态prev_state是非零,通常主调度之前会提前设置当前进程的
                                  // 运行状态为 TASK_INTERRUPTIBLE 或者 TASK_UNINTERRUPTIBLE) if (signal_pending_state(prev_state, prev)) { WRITE_ONCE(prev->__state, TASK_RUNNING); } else { prev->sched_contributes_to_load = (prev_state & TASK_UNINTERRUPTIBLE) && !(prev_state & TASK_NOLOAD) && !(prev->flags & PF_FROZEN); if (prev->sched_contributes_to_load) rq->nr_uninterruptible++; deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK); // 设置 prev->on_rq 为0,表示此任务不在运行队列里;并把此任务从运行队列移除 if (prev->in_iowait) { atomic_inc(&rq->nr_iowait); delayacct_blkio_start(); } } switch_count = &prev->nvcsw; // Context voluntary switch counter,此任务上下文主动切换计数 } // 抢占调度,要切换的任务还是在运行队列 next = pick_next_task(rq, prev, &rf); // 根据prev任务的调度类选一个最高优先级的任务返回 clear_tsk_need_resched(prev); // 清楚prev任务的 TIF_NEED_RESCHED 的标记 clear_preempt_need_resched(); #ifdef CONFIG_SCHED_DEBUG rq->last_seen_need_resched_ns = 0; #endif if (likely(prev != next)) { rq->nr_switches++; // 队列内的任务切换次数统计 RCU_INIT_POINTER(rq->curr, next); ++*switch_count; // 任务上下文切换统计 migrate_disable_switch(rq, prev); psi_sched_switch(prev, next, !task_on_rq_queued(prev)); trace_sched_switch(sched_mode & SM_MASK_PREEMPT, prev, next); /* Also unlocks the rq: */ rq = context_switch(rq, prev, next, &rf); } else { rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP); rq_unpin_lock(rq, &rf); __balance_callbacks(rq); raw_spin_rq_unlock_irq(rq); } }

上下文切换函数 context_switch,切换内存描述符和MMU的页表首地址、CPU寄存器。

用户线程有用户态和内存态,内核线程只有内核态。

task_struct 有两个成员,分别是 mm 和 active_mm,对于 用户线程 来说 , active_mm 字段 与 mm 字段 指向同一个 " 内存描述符 " ;但对于 " 内核线程 " 来说 , mm 字段 指向 空指针 , active_mm 字段 指向同一个CPU最近运行的用户线程的 " 内存描述符 " ;

/*
 * context_switch - switch to the new MM and the new thread's register state.
 */
static __always_inline struct rq *
context_switch(struct rq *rq, struct task_struct *prev,
           struct task_struct *next, struct rq_flags *rf)
{
    prepare_task_switch(rq, prev, next);

    /*
     * 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);

    /*
     * kernel -> kernel   lazy + transfer active
     *   user -> kernel   lazy + mmgrab() active
     *
     * kernel ->   user   switch + mmdrop() active
     *   user ->   user   switch
     */
    if (!next->mm) {                                // 切换到内核线程
        enter_lazy_tlb(prev->active_mm, next);

        next->active_mm = prev->active_mm; // 指向要被切换出去线程的内存描述符
        if (prev->mm)                           // 被切换出去线程属于用户线程
            mmgrab(prev->active_mm); // 由于被切换出去线程的内存描述符被要运行的内核线程使用,内存描述符的引用计数加一
        else
            prev->active_mm = NULL; // 被切换出去线程属于内核线程,其内存描述符指向NULL
    } else {                                        // 切换到用户线程
        membarrier_switch_mm(rq, prev->active_mm, next->mm);
        /*
         * sys_membarrier() requires an smp_mb() between setting
         * rq->curr / membarrier_switch_mm() and returning to userspace.
         *
         * The below provides this either through switch_mm(), or in
         * case 'prev->active_mm == next->mm' through
         * finish_task_switch()'s mmdrop().
         */
        switch_mm_irqs_off(prev->active_mm, next->mm, next); // 设置 MMU 的页表首地址为准备运行的用户线程的页表首地址

        if (!prev->mm) {                        // from kernel
            /* will mmdrop() in finish_task_switch(). */
            rq->prev_mm = prev->active_mm;
            prev->active_mm = NULL;
        }
    }

    rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);

    prepare_lock_switch(rq, next, rf);

    /* Here we just switch the register state and the stack. */
    switch_to(prev, next, prev); // 切换CPU寄存器
    barrier();

    return finish_task_switch(prev);
}

 

标签:__,rq,schedule,mm,next,---,switch,active,prev
From: https://www.cnblogs.com/god-of-death/p/17811131.html

相关文章

  • 2023-2024-1 学号20231315第六周学习总结
    学期:2023-2024-1学号:20231315《计算机基础与程序设计》第五周学习总结作业信息这个作业属于哪个课程2023-2024-1《计算机基础与程序设计》这个作业要求在哪里2023-2024-1《计算机基础与程序设计》这个作业的目标学习计算机科学概论第7章和《C语言程序设计》第5......
  • HTML笔记
    2023-11-051、HTML(超文本标记语言)是一种标记语言(标记标签(来描述网页)),而不是编程语言。HTML文档(Web页面)包含了HTML标签和文本内容。2、HTML标签(HTML标记标签)尖括号包围的关键词。 eg.<html>成对出现。  eg.<b>(开始标签(开放标签))和<b>(结束标签(闭合标签))      ......
  • 2023-2024-1 20231306 《计算机基础与程序设计》第六周学习总结
    这个作业属于哪个课程2023-2024-1-计算机基础与程序设计这个作业要求在哪里2023-2024-1计算机基础与程序设计第六周作业这个作业的目标Polya如何解决问题、简单类型与组合类型、复合数据结构、查找与排序算法、算法复杂度、递归、代码安全作业正文《计算机......
  • js把json字符串转成json数组
    如何将JSON字符串转换为JSON数组。假设你有以下JSON字符串,它表示一个简单的数组,其中包含两个对象:'[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]'要将这个JSON字符串转换为JavaScript中的数组对象,你可以使用JSON.parse()方法。这个方法接受一个J......
  • Laravel简单模型使用
    1,创建模型phpartisanmake:modelBook-m创建模型book并生成迁移文件2,Book迁移文件publicfunctionup(){Schema::create('books',function(Blueprint$table){$table->increments('id');$table->string('title');$ta......
  • 7.接雨水
    题目概述:给定一些柱子的高度,问这些柱子能够接到多少雨水解题思路:将每个位置都想象有一个木桶,接到雨水的量=木桶体积-柱子体积。木桶的高度由前后缀数组中的较小者决定时间复杂度:\(O(n)\)代码:classSolution{publicinttrap(int[]height){intn=height.len......
  • 题解 P6878 [JOI 2020 Final] JJOOII 2
    好久没写题解,水一篇。题意题意显然。分析看到这道题,我们就应该进行一个小贪心,对于最左边某一字符,直到最右边的这一字符,我们不会在中间删除同样的字符,不然则可以保留这一字符,将两边往内缩。也就是说,我们确定了最左边的J后,那么留下最后一个J必然是当前这个J的后面的第\(......
  • js怎么把json字符串转化为一个对象
    在JavaScript中,如果你有一个JSON字符串,你可以使用JSON.parse()方法将其转换成一个JavaScript对象。例如,如果你有以下的JSON字符串:'{"id":1,"name":"Alice"}'你可以使用以下的代码将其转换成一个JavaScript对象://JSON字符串varjsonString='{"id":1,"name"......
  • 2023-2024 20231310 《计算机基础与程序设计》 第六周学习总结
    作业信息这个作业属于哪个课程https://edu.cnblogs.com/campus/besti/2023-2024-1-CFAP这个作业要求在哪里https://www.cnblogs.com/rocedu/p/9577842.html#WEEK06这个作业的目标《计算机科学概论》第七章,《C语言程序设计》第五章作业正文教材学习内容总结......
  • Java 操作 XML(15)--XOM 使用
    XOM是一个面向对象的XMLAPI,有点像DOM风格,但是许多功能使XOM与众不同,其中最主要的是严格维护内存对象中的不变性,以便始终可以将XOM实例序列化以更正XML。本文主要介绍使用XOM处理XML,文中所使用到的软件版本:Java1.8.0_341、XOM1.3.9。1、简介XOM被设计成易于学习和......