9159金沙游艺场-奥门金沙游艺场-[欢迎您]

9159金沙游艺场为您呈现最新的面貌!,目前注册送体验金,欢迎您将成为影视娱乐行业新的风向标,是亚洲的优质娱乐先锋。

linux调节源码

日期:2020-01-04编辑作者:办公软件

linux调治器源码解析,linux调整源码

本文为原创,转发请声明:

 

引言

  在此以前的随笔已经将调解器的数据结构、起首化、参加进度都进展了深入分析,那篇小说将紧要表达调治器是什么在前后相继牢固运转的场馆下开展进度调整的。

 

系统反应计时器

  因为大家根本教授的是调整器,而会涉嫌到有的系统电磁打点计时器的学识,这里大家大约讲授一下基石中停车计时器是哪些组织,又是哪些通过通过计时器达成了调节器的间距调整。首先我们先看一下根本放大计时器的框架

图片 1

  在基本中,会使用strut clock_event_device构造描述硬件上的沙漏,各个硬件测量时间的装置都有其和好的精度,会依据精度每间距风华正茂段时间产生一个石英钟中断。而系统会让各样CPU使用一个tick_device描述系统当下应用的硬件机械漏刻(因为各类CPU皆有其和好的运作队列卡塔尔(قطر‎,通过tick_device所使用的硬件机械钟中断实行石英钟滴答(jiffiesState of Qatar的丰富(只会有一个CPU担任那事State of Qatar,并且在制动踏板中也会调用调整器,而小编辈在驱动中常用的低精度测量时间的装置正是通过推断jiffies完成的。而当使用高精度沙漏(hrtimer卡塔尔国时,情形则不平等,hrtimer会生成二个惯常的高精度沙漏,在这里个沙漏中回调函数是调整器,其设置的间距时间相同的时间钟滴答同样。

  所以在系统中,每三遍石英钟滴答都会使调整器判定三遍是否要求开展调治。

 

石英石英钟中断

  当时钟发(Zhong Fa卡塔尔(قطر‎生中断时,首先会调用的是tick_handle_periodic(卡塔尔(قطر‎函数,在这里函数中又首要实行tick_periodic(State of Qatar函数实行操作。大家先看一下tick_handle_periodic()函数:

 1 void tick_handle_periodic(struct clock_event_device *dev)
 2 {
 3     /* 获取当前CPU */
 4     int cpu = smp_processor_id();
 5     /* 获取下次时钟中断执行时间 */
 6     ktime_t next = dev->next_event;
 7 
 8     tick_periodic(cpu);
 9     
10     /* 如果是周期触发模式,直接返回 */
11     if (dev->mode != CLOCK_EVT_MODE_ONESHOT)
12         return;
13 
14     /* 为了防止当该函数被调用时,clock_event_device中的计时实际上已经经过了不止一个tick周期,这时候,tick_periodic可能被多次调用,使得jiffies和时间可以被正确地更新。 */
15     for (;;) {
16         /*
17          * Setup the next period for devices, which do not have
18          * periodic mode:
19          */
20         /* 计算下一次触发时间 */
21         next = ktime_add(next, tick_period);
22 
23         /* 设置下一次触发时间,返回0表示成功 */
24         if (!clockevents_program_event(dev, next, false))
25             return;
26         /*
27          * Have to be careful here. If we're in oneshot mode,
28          * before we call tick_periodic() in a loop, we need
29          * to be sure we're using a real hardware clocksource.
30          * Otherwise we could get trapped in an infinite(无限的)
31          * loop, as the tick_periodic() increments jiffies,
32          * which then will increment time, possibly causing
33          * the loop to trigger again and again.
34          */
35         if (timekeeping_valid_for_hres())
36             tick_periodic(cpu);
37     }
38 }

  此函数首要专门的学问是施行tick_periodic(卡塔尔国函数,然后决断石英钟中断是单触发格局或然循环触发方式,假若是循环触发方式,则直接再次来到,若是是单触发方式,则实施如下操作:

  • 总括下二回接触时间
  • 安装后一次接触时间
  • 如果设置下一次触及时间退步,则基于timekeeper等待下次tick_periodic(State of Qatar函数实施时间。
  • 回去第一步

 

  而在tick_periodic(State of Qatar函数中,程序主要实行路径为tick_periodic()->update_process_times()->scheduler_tick()。最后的scheduler_tick(卡塔尔(قطر‎函数则是跟调解相关的第黄金时代函数。大家在此现实先看看tick_periodic()函数和update_process_times()函数:

 1 /* tick_device 周期性调用此函数
 2  * 更新jffies和当前进程
 3  * 只有一个CPU是负责更新jffies的,其他的CPU只会更新当前自己的进程
 4  */
 5 static void tick_periodic(int cpu)
 6 {
 7 
 8     if (tick_do_timer_cpu == cpu) {
 9         /* 当前CPU负责更新时间 */
10         write_seqlock(&jiffies_lock);
11 
12         /* Keep track of the next tick event */
13         tick_next_period = ktime_add(tick_next_period, tick_period);
14 
15         /* 更新 jiffies计数,jiffies += 1 */
16         do_timer(1);
17         write_sequnlock(&jiffies_lock);
18         /* 更新墙上时间,就是我们生活中的时间 */
19         update_wall_time();
20     }
21     /* 更新当前进程信息,调度器主要函数 */
22     update_process_times(user_mode(get_irq_regs()));
23     profile_tick(CPU_PROFILING);
24 }
25 
26 
27 
28 
29 void update_process_times(int user_tick)
30 {
31     struct task_struct *p = current;
32     int cpu = smp_processor_id();
33 
34     /* Note: this timer irq context must be accounted for as well. */
35     /* 更新当前进程的内核态和用户态占用率 */
36     account_process_tick(p, user_tick);
37     /* 检查有没有定时器到期,有就运行到期定时器的处理 */
38     run_local_timers();
39     rcu_check_callbacks(cpu, user_tick);
40 #ifdef CONFIG_IRQ_WORK
41     if (in_irq())
42         irq_work_tick();
43 #endif
44     /* 调度器的tick */
45     scheduler_tick();
46     run_posix_cpu_timers(p);
47 }

  这多少个函数首要工作为将jiffies加1、更新系统的墙上时间、更新当前历程的内核态和客户态的CPU占用率、检查是还是不是有电火花计时器到期,运营到期的电磁打点计时器。当实行完那一个操作后,就到了最要害的scheduler_tick()函数,而scheduler_tick(卡塔尔(قطر‎函数首要做什么样啊,便是创新CPU和脚下扩充的部分多少,然后依据当前路程的调解类,调用task_tick(卡塔尔国函数。这里普通进度调节类的task_tick()是task_tick_fair()函数。

 1 void scheduler_tick(void)
 2 {
 3     /* 获取当前CPU的ID */
 4     int cpu = smp_processor_id();
 5     /* 获取当前CPU的rq队列 */
 6     struct rq *rq = cpu_rq(cpu);
 7     /* 获取当前CPU的当前运行程序,实际上就是current */
 8     struct task_struct *curr = rq->curr;
 9     /* 更新CPU调度统计中的本次调度时间 */
10     sched_clock_tick();
11 
12     raw_spin_lock(&rq->lock);
13     /* 更新该CPU的rq运行时间 */
14     update_rq_clock(rq);
15     curr->sched_class->task_tick(rq, curr, 0);
16     /* 更新CPU的负载 */
17     update_cpu_load_active(rq);
18     raw_spin_unlock(&rq->lock);
19 
20     perf_event_task_tick();
21 
22 #ifdef CONFIG_SMP
23     rq->idle_balance = idle_cpu(cpu);
24     trigger_load_balance(rq);
25 #endif
26     /* rq->last_sched_tick = jiffies; */
27     rq_last_tick_reset(rq);
28 }
29 
30 
31 
32 
33 /*
34  * CFS调度类的task_tick()
35  */
36 static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
37 {
38     struct cfs_rq *cfs_rq;
39     struct sched_entity *se = &curr->se;
40     /* 向上更新进程组时间片 */
41     for_each_sched_entity(se) {
42         cfs_rq = cfs_rq_of(se);
43         /* 更新当前进程运行时间,并判断是否需要调度此进程 */
44         entity_tick(cfs_rq, se, queued);
45     }
46 
47     if (numabalancing_enabled)
48         task_tick_numa(rq, curr);
49 
50     update_rq_runnable_avg(rq, 1);
51 }

  鲜明,到此处最珍视的函数应该是entity_tick(State of Qatar,因为是以此函数决定了当前进度是还是不是须要调整出去。大家必得先鲜明一点就是,CFS调节计谋是应用红黑树以进度的vruntime为键值举办公司的,进程的vruntime越小越在红黑树的左侧,而每趟调治的下一个目的正是红黑树最左边包车型地铁结点上的经过。而当进行运作时,其vruntime是随着实际运作时刻而充实的,不过区别权重的长河其vruntime扩大的速率分化,正在运行的进程的权重约大(优先级越高卡塔尔(قطر‎,其vruntime扩展的速率越慢,所以其所攻克的CPU时间越来越多。而每一遍石英钟中断的时候,在entity_tick(卡塔尔函数中都会更新当前历程的vruntime值。当进度未有处于CPU上运转时,其vruntime是维系不改变的。

 1 static void
 2 entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
 3 {
 4     /*
 5      * Update run-time statistics of the 'current'.
 6      */
 7     /* 更新当前进程运行时间,包括虚拟运行时间 */
 8     update_curr(cfs_rq);
 9 
10     /*
11      * Ensure that runnable average is periodically updated.
12      */
13     update_entity_load_avg(curr, 1);
14     update_cfs_rq_blocked_load(cfs_rq, 1);
15     update_cfs_shares(cfs_rq);
16 
17 #ifdef CONFIG_SCHED_HRTICK
18     /*
19      * queued ticks are scheduled to match the slice, so don't bother
20      * validating it and just reschedule.
21      */
22     /* 若queued为1,则当前运行队列的运行进程需要调度 */
23     if (queued) {
24         /* 标记当前进程需要被调度出去 */
25         resched_curr(rq_of(cfs_rq));
26         return;
27     }
28     /*
29      * don't let the period tick interfere with the hrtick preemption
30      */
31     if (!sched_feat(DOUBLE_TICK) && hrtimer_active(&rq_of(cfs_rq)->hrtick_timer))
32         return;
33 #endif
34     /* 检查是否需要调度 */
35     if (cfs_rq->nr_running > 1)
36         check_preempt_tick(cfs_rq, curr);
37 }

  之后的篇章会详细说说CFS关于进程的vruntime的管理,以后只必要精晓是那样就好,在entity_tick(卡塔尔中,首先会更新当前路程的莫过于运营时刻和编造运营时刻,这里很要紧,因为要利用更新后的那几个数据去判定是不是须求被调解。在entity_tick(卡塔尔函数中最前边的check_preempt_tick(卡塔尔国函数就是用来决断进度是还是不是要求被调整的,其判别的正统有三个:

  • 先判定当前进度的实际上运作时刻是不是超越CPU分配给这几个进程的CPU时间,假如凌驾,则要求调整。
  • 再判定当前经过的vruntime是还是不是高于下个经过的vruntime,假如超越,则须求调解。

  清楚了这八个正式,check_preempt_tick(卡塔尔(قطر‎的代码则很好明白了。

 1 /*
 2  * 检查当前进程是否需要被抢占
 3  * 判断方法有两种,一种就是判断当前进程是否超过了CPU分配给它的实际运行时间
 4  * 另一种就是判断当前进程的虚拟运行时间是否大于下个进程的虚拟运行时间
 5  */
 6 static void
 7 check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
 8 {
 9     /* ideal_runtime为进程应该运行的时间
10      * delta_exec为进程增加的实际运行时间
11      * 如果delta_exec超过了ideal_runtime,表示该进程应该让出CPU给其他进程
12      */
13     unsigned long ideal_runtime, delta_exec;
14     struct sched_entity *se;
15     s64 delta;
16 
17 
18     /* slice为CFS队列中所有进程运行一遍需要的实际时间 */
19     /* ideal_runtime保存的是CPU分配给当前进程一个周期内实际的运行时间,计算公式为:  一个周期内进程应当运行的时间 = 一个周期内队列中所有进程运行一遍需要的时间 * 当前进程权重 / 队列总权重
20      * delta_exec保存的是当前进程增加使用的实际运行时间
21      */
22     ideal_runtime = sched_slice(cfs_rq, curr);
23     delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
24     if (delta_exec > ideal_runtime) {
25         /* 增加的实际运行实际 > 应该运行实际,说明需要调度出去 */
26         resched_curr(rq_of(cfs_rq));
27         /*
28          * The current task ran long enough, ensure it doesn't get
29          * re-elected due to buddy favours.
30          */
31         /* 清空cfs_rq队列的last,next,skip指针 */
32         clear_buddies(cfs_rq, curr);
33         return;
34     }
35 
36     /*
37      * Ensure that a task that missed wakeup preemption by a
38      * narrow margin doesn't have to wait for a full slice.
39      * This also mitigates buddy induced latencies under load.
40      */
41     if (delta_exec < sysctl_sched_min_granularity)
42         return;
43     /* 获取下一个调度进程的se */
44     se = __pick_first_entity(cfs_rq);
45     /* 当前进程的虚拟运行时间 - 下个进程的虚拟运行时间 */
46     delta = curr->vruntime - se->vruntime;
47 
48     /* 当前进程的虚拟运行时间 大于 下个进程的虚拟运行时间,说明这个进程还可以继续运行 */
49     if (delta < 0)
50         return;
51 
52     if (delta > ideal_runtime)
53         /* 当前进程的虚拟运行时间 小于 下个进程的虚拟运行时间,说明下个进程比当前进程更应该被CPU使用,resched_curr()函数用于标记当前进程需要被调度出去 */
54         resched_curr(rq_of(cfs_rq));
55 }
56 
57 
58 
59 
60 /*
61  * resched_curr - mark rq's current task 'to be rescheduled now'.
62  *
63  * On UP this means the setting of the need_resched flag, on SMP it
64  * might also involve a cross-CPU call to trigger the scheduler on
65  * the target CPU.
66  */
67 /* 标记当前进程需要调度,将当前进程的thread_info->flags设置TIF_NEED_RESCHED标记 */
68 void resched_curr(struct rq *rq)
69 {
70     struct task_struct *curr = rq->curr;
71     int cpu;
72 
73     lockdep_assert_held(&rq->lock);
74 
75     /* 检查当前进程是否已经设置了调度标志,如果是,则不用再设置一遍,直接返回 */
76     if (test_tsk_need_resched(curr))
77         return;
78 
79     /* 根据rq获取CPU */
80     cpu = cpu_of(rq);
81     /* 如果CPU = 当前CPU,则设置当前进程需要调度标志 */
82     if (cpu == smp_processor_id()) {
83         /* 设置当前进程需要被调度出去的标志,这个标志保存在进程的thread_info结构上 */
84         set_tsk_need_resched(curr);
85         /* 设置CPU的内核抢占 */
86         set_preempt_need_resched();
87         return;
88     }
89 
90     /* 如果不是处于当前CPU上,则设置当前进程需要调度,并通知其他CPU */
91     if (set_nr_and_not_polling(curr))
92         smp_send_reschedule(cpu);
93     else
94         trace_sched_wake_idle_without_ipi(cpu);
95 }

  好了,到此地其实借使经过须要被调解,则已经被标志,假如经过没有必要被调整,则继续试行。这里大家也可能有疑问,只标识了经过需求被调节,然而为何并未当真管理它?其实根据自个儿的博文linux调节器源码分析

概述(意气风发卡塔尔(قطر‎所说,进程调节的发出机遇之豆蔻年华就是发出在暂停重回时,这里是在汇编代码中贯彻的,而我们精通这里大家是时钟中断实践上述的那几个操作的,当施行完这几个后,从石英钟中断再次来到去的时候,会调用到汇编函数ret_from_sys_call,在这里个函数中会先检查调整标识被置位,若是被置位,则跳转至schedule(State of Qatar,而schedule(卡塔尔(قطر‎最后调用到__schedule(State of Qatar那么些函数实行处理。

  1 static void __sched __schedule(void)
  2 {
  3     /* prev保存换出进程(也就是当前进程),next保存换进进程 */
  4     struct task_struct *prev, *next;
  5     unsigned long *switch_count;
  6     struct rq *rq;
  7     int cpu;
  8 
  9 need_resched:
 10     /* 禁止抢占 */
 11     preempt_disable();
 12     /* 获取当前CPU ID */
 13     cpu = smp_processor_id();
 14     /* 获取当前CPU运行队列 */
 15     rq = cpu_rq(cpu);
 16     rcu_note_context_switch(cpu);
 17     prev = rq->curr;
 18 
 19     schedule_debug(prev);
 20 
 21     if (sched_feat(HRTICK))
 22         hrtick_clear(rq);
 23 
 24     /*
 25      * Make sure that signal_pending_state()->signal_pending() below
 26      * can't be reordered with __set_current_state(TASK_INTERRUPTIBLE)
 27      * done by the caller to avoid the race with signal_wake_up().
 28      */
 29     smp_mb__before_spinlock();
 30     /* 队列上锁 */
 31     raw_spin_lock_irq(&rq->lock);
 32     /* 当前进程非自愿切换次数 */
 33     switch_count = &prev->nivcsw;
 34     
 35     /*
 36      * 当内核抢占时会置位thread_info的preempt_count的PREEMPT_ACTIVE位,调用schedule()之后会清除,PREEMPT_ACTIVE置位表明是从内核抢占进入到此的
 37      * preempt_count()是判断thread_info的preempt_count整体是否为0
 38      * prev->state大于0表明不是TASK_RUNNING状态
 39      *
 40      */
 41     if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {    
 42         /* 当前进程不为TASK_RUNNING状态并且不是通过内核态抢占进入调度 */
 43         if (unlikely(signal_pending_state(prev->state, prev))) {
 44             /* 有信号需要处理,置为TASK_RUNNING */
 45             prev->state = TASK_RUNNING;
 46         } else {
 47             /* 没有信号挂起需要处理,会将此进程移除运行队列 */
 48             /* 如果代码执行到此,说明当前进程要么准备退出,要么是处于即将睡眠状态 */
 49             deactivate_task(rq, prev, DEQUEUE_SLEEP);
 50             prev->on_rq = 0;
 51 
 52             /*
 53              * If a worker went to sleep, notify and ask workqueue
 54              * whether it wants to wake up a task to maintain
 55              * concurrency.
 56              */
 57             if (prev->flags & PF_WQ_WORKER) {
 58                 /* 如果当前进程处于一个工作队列中 */
 59                 struct task_struct *to_wakeup;
 60 
 61                 to_wakeup = wq_worker_sleeping(prev, cpu);
 62                 if (to_wakeup)
 63                     try_to_wake_up_local(to_wakeup);
 64             }
 65         }
 66         switch_count = &prev->nvcsw;
 67     }
 68 
 69     /* 更新rq运行队列时间 */
 70     if (task_on_rq_queued(prev) || rq->skip_clock_update < 0)
 71         update_rq_clock(rq);
 72 
 73     /* 获取下一个调度实体,这里的next的值会是一个进程,而不是一个调度组,在pick_next_task会递归选出一个进程 */
 74     next = pick_next_task(rq, prev);
 75     /* 清除当前进程的thread_info结构中的flags的TIF_NEED_RESCHED和PREEMPT_NEED_RESCHED标志位,这两个位表明其可以被调度调出(因为这里已经调出了,所以这两个位就没必要了) */
 76     clear_tsk_need_resched(prev);
 77     clear_preempt_need_resched();
 78     rq->skip_clock_update = 0;
 79 
 80     if (likely(prev != next)) {
 81         /* 该CPU进程切换次数加1 */
 82         rq->nr_switches++;
 83         /* 该CPU当前执行进程为新进程 */
 84         rq->curr = next;
 85         
 86         ++*switch_count;
 87         
 88         /* 这里进行了进程上下文的切换 */
 89         context_switch(rq, prev, next); /* unlocks the rq */
 90         /*
 91          * The context switch have flipped the stack from under us
 92          * and restored the local variables which were saved when
 93          * this task called schedule() in the past. prev == current
 94          * is still correct, but it can be moved to another cpu/rq.
 95          */
 96         /* 新的进程有可能在其他CPU上运行,重新获取一次CPU和rq */
 97         cpu = smp_processor_id();
 98         rq = cpu_rq(cpu);
 99     }
100     else
101         raw_spin_unlock_irq(&rq->lock);        /* 这里意味着下个调度的进程就是当前进程,释放锁不做任何处理 */
102     /* 上下文切换后的处理 */
103     post_schedule(rq);
104 
105     /* 重新打开抢占使能但不立即执行重新调度 */
106     sched_preempt_enable_no_resched();
107     if (need_resched())
108         goto need_resched;
109 }

  在__schedule(State of Qatar中,每一步的功效注释已经写得很详细了,接收下贰个进程的天职在__schedule(卡塔尔(قطر‎中提交了pick_next_task(State of Qatar函数,而经过切换则交由了context_switch(State of Qatar函数。大家先看看pick_next_task(卡塔尔函数是怎么着抉择下三个进程的:

 1 static inline struct task_struct *
 2 pick_next_task(struct rq *rq, struct task_struct *prev)
 3 {
 4     const struct sched_class *class = &fair_sched_class;
 5     struct task_struct *p;
 6 
 7     /*
 8      * Optimization: we know that if all tasks are in
 9      * the fair class we can call that function directly:
10      */
11 
12     if (likely(prev->sched_class == class && rq->nr_running == rq->cfs.h_nr_running)) {
13         /* 所有进程都处于CFS运行队列中,所以就直接使用cfs的调度类 */
14         p = fair_sched_class.pick_next_task(rq, prev);
15         if (unlikely(p == RETRY_TASK))
16             goto again;
17 
18         /* assumes fair_sched_class->next == idle_sched_class */
19         if (unlikely(!p))
20             p = idle_sched_class.pick_next_task(rq, prev);
21 
22         return p;
23     }
24 
25 again:
26     /* 在其他调度类中包含有其他进程,从最高优先级的调度类迭代到最低优先级的调度类,并选择最优的进程运行 */
27     for_each_class(class) {
28         p = class->pick_next_task(rq, prev);
29         if (p) {
30             if (unlikely(p == RETRY_TASK))
31                 goto again;
32             return p;
33         }
34     }
35 
36     BUG(); /* the idle class will always have a runnable task */
37 }

  在pick_next_task(卡塔尔(قطر‎中全然反映了经过优先级的定义,首先会先推断是或不是富有进度都处在cfs队列中,如若不是,则表明有比日常进程更加高优先级的长河(蕴含实时进程卡塔尔(قطر‎。内核中是将调解类重优先级高到低进行排列,然后选用时从高耸云霄优先级的调节类以前找是不是有进程须要调整,若无会转到下黄金时代优先级调整类,在代码27行所反映,27行进行是

#define for_each_class(class) 
   for (class = sched_class_highest; class; class = class->next)

  而调节类的预先级依次为

调度类优先级顺序: stop_sched_class -> dl_sched_class -> rt_sched_class -> fair_sched_class -> idle_sched_class

  在pick_next_task(卡塔尔国函数中回到了选定的历程的历程描述符,接下去就能够调用context_switch(卡塔尔进行进度切换了。

 1 static inline void
 2 context_switch(struct rq *rq, struct task_struct *prev,
 3            struct task_struct *next)
 4 {
 5     struct mm_struct *mm, *oldmm;
 6 
 7     prepare_task_switch(rq, prev, next);
 8 
 9     mm = next->mm;
10     oldmm = prev->active_mm;
11     /*
12      * For paravirt, this is coupled with an exit in switch_to to
13      * combine the page table reload and the switch backend into
14      * one hypercall.
15      */
16     arch_start_context_switch(prev);
17 
18     if (!mm) {
19         /* 如果新进程的内存描述符为空,说明新进程为内核线程 */
20         next->active_mm = oldmm;
21         atomic_inc(&oldmm->mm_count);
22         /* 通知底层不需要切换虚拟地址空间
23          *     if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK)
24          *        this_cpu_write(cpu_tlbstate.state, TLBSTATE_LAZY);
25          */
26         enter_lazy_tlb(oldmm, next);
27     } else
28         /* 切换虚拟地址空间 */
29         switch_mm(oldmm, mm, next);
30 
31     if (!prev->mm) {
32         /* 如果被切换出去的进程是内核线程 */
33         prev->active_mm = NULL;
34         /* 归还借用的oldmm  */
35         rq->prev_mm = oldmm;
36     }
37     /*
38      * Since the runqueue lock will be released by the next
39      * task (which is an invalid locking op but in the case
40      * of the scheduler it's an obvious special-case), so we
41      * do an early lockdep release here:
42      */
43     spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
44 
45     context_tracking_task_switch(prev, next);
46     
47     /* 切换寄存器和内核栈,还会重新设置current为切换进去的进程 */
48     switch_to(prev, next, prev);
49 
50     /* 同步 */
51     barrier();
52     /*
53      * this_rq must be evaluated again because prev may have moved
54      * CPUs since it called schedule(), thus the 'rq' on its stack
55      * frame will be invalid.
56      */
57     finish_task_switch(this_rq(), prev);
58 }

  到此地全体进程的取舍和切换就已经到位了。

 

总结

  整个调整器大约原理和源码已经解析形成,其他更加多细节,如CFS的部分酌量和处理,实时进程的管理等,就要此外小说进行详细分解。

 

本文为原创,转发请注解: 引言 早先的篇章已经将调治器的数据布局、开始化...

本文由9159金沙游艺场-奥门金沙游艺场-[欢迎您]发布于办公软件,转载请注明出处:linux调节源码

关键词:

python静态方法实例9159金沙游艺场

python静态方法实例,python静态实例 本文实例讲述了python静态方法。分享给大家供大家参考。 具体实现方法如下: 复...

详细>>

CSS中父对象的内边距是否对子对象的外边距造成

CSS中父对象的内边距是否对子对象的外边距造成影响,css对子 CSS中父对象的内边距是否对子对象的外边距造成影响...

详细>>

CentOS7安装JAVA【9159金沙游艺场】

CentOS7安装JAVA 第一步:查看Linux自带的JDK是否已安装 安装好的CentOS会自带OpenJdk,用命令 java -version ,会有下面的信息:...

详细>>

Python连接mssql数据库编码问题解决方法9159金沙游

Python连接mssql数据库编码问题解决方法,pythonmssql python一直对中文支持的不好,最近老遇到编码问题,而且几乎没有通...

详细>>