内核代码阅读(25) - 强制调度

简介: 强制调度

强制调度

进程的强制调度是通过设置目标进程的task_struct中的need_resched来完成的。

什么时候要发生强制调度呢?有三种情况:

1) 进程运行的时间过长,这是通过时钟中断服务程序来保证。

2) 唤醒一个睡眠的进程,发现这个进程比当前进程更有资格运行。

3) 一个进程通过系统调用改变调度策略。会立即引发调度。

在设置了need_resched之后,到真正的触发schedule(),到目标进程开始被调度起来,之间有一段不确定的延迟,"dispatch latency"。

第一种情况:时钟中断update_process_times

do_timer_interrupt() -> do_timer() -> update_process_times()
    void update_process_times(int user_tick)
    {
        struct task_struct *p = current;
        int cpu = smp_processor_id(), system = user_tick ^ 1;
        update_one_process(p, user_tick, system, cpu);
        if (p->pid) {
                if (--p->counter <= 0) {
                        p->counter = 0;
                        p->need_resched = 1;
                }
                if (p->nice > 0)
                        kstat.per_cpu_nice[cpu] += user_tick;
                else
                        kstat.per_cpu_user[cpu] += user_tick;
                kstat.per_cpu_system[cpu] += system;
        } else if (local_bh_count(cpu) || local_irq_count(cpu) > 1)
                kstat.per_cpu_system[cpu] += system;
    }
1) if (--p->counter <= 0) {
   将当前正在执行的进程的时间片减1,如果小于0,则设置need_resched

第二种情况:唤醒睡眠的进程wake_up_process

inline void wake_up_process(struct task_struct * p)
    {
        unsigned long flags;
        spin_lock_irqsave(&runqueue_lock, flags);
        p->state = TASK_RUNNING;
        if (task_on_runqueue(p))
                goto out;
        add_to_runqueue(p);
        reschedule_idle(p);
    out:
        spin_unlock_irqrestore(&runqueue_lock, flags);
    }
唤醒的操作是:
1) 把进程的状态改成TASK_RUNNING。
2) 加入runqueue队列。
3) 调用reschedule_idle

唤醒一个进程后,主动的触发调度

static void reschedule_idle(struct task_struct * p)
    {
        struct task_struct *tsk;
        tsk = cpu_curr(this_cpu);
        if (preemption_goodness(tsk, p, this_cpu) > 1)
                tsk->need_resched = 1;
    }
如果当前进程的权重低于被唤醒的进程,则把被唤醒进程的need_resched置位。

第三种情况:进程主动让出CPU, sched_setscheduler()和sched_yield()

sched_setscheduler

asmlinkage long sys_sched_setscheduler(pid_t pid, int policy, 
                                      struct sched_param *param)
    {
        return setscheduler(pid, policy, param);
    }
asmlinkage long sys_sched_setparam(pid_t pid, struct sched_param *param)
    {
        return setscheduler(pid, -1, param);
    }
    static int setscheduler(pid_t pid, int policy, 
                        struct sched_param *param)
    {
        struct sched_param lp;
        struct task_struct *p;
        int retval;
        retval = -EINVAL;
        if (!param || pid < 0)
                goto out_nounlock;
        retval = -EFAULT;
        if (copy_from_user(&lp, param, sizeof(struct sched_param)))
                goto out_nounlock;
        read_lock_irq(&tasklist_lock);
        spin_lock(&runqueue_lock);
        p = find_process_by_pid(pid);
        retval = -ESRCH;
        if (!p)
                goto out_unlock;
        if (policy < 0)
                policy = p->policy;
        else {
                retval = -EINVAL;
                if (policy != SCHED_FIFO && policy != SCHED_RR &&
                                policy != SCHED_OTHER)
                        goto out_unlock;
        }
        retval = -EINVAL;
        if (lp.sched_priority < 0 || lp.sched_priority > 99)
                goto out_unlock;
        if ((policy == SCHED_OTHER) != (lp.sched_priority == 0))
                goto out_unlock;
        retval = -EPERM;
        if ((policy == SCHED_FIFO || policy == SCHED_RR) && 
            !capable(CAP_SYS_NICE))
                goto out_unlock;
        if ((current->euid != p->euid) && (current->euid != p->uid) &&
            !capable(CAP_SYS_NICE))
                goto out_unlock;
        retval = 0;
        p->policy = policy;
        p->rt_priority = lp.sched_priority;
        if (task_on_runqueue(p))
                move_first_runqueue(p);
        current->need_resched = 1;
    out_unlock:
        spin_unlock(&runqueue_lock);
        read_unlock_irq(&tasklist_lock);
    out_nounlock:
        return retval;
    }
这个函数很简单,找到pid的进程,然后设置policy,然后把当前进程的need_resched置位。在系统调用返回的时候会发生一次调度。
另外,目标进程已经在runqueue列表中了,就把这个进程移动到runqueue的头部。

sched_yield

asmlinkage long sys_sched_yield(void)
    {
        int nr_pending = nr_running;
        nr_pending--;
        if (nr_pending) {
                if (current->policy == SCHED_OTHER)
                        current->policy |= SCHED_YIELD;
                current->need_resched = 1;
        }
        return 0;
    }
如果当前的runqueue只有一个一进程,则直接返回。
否则,如果当前进程的调度策略是SCHED_OTHER,则设置策略SCHED_YIELD。
否则,把当前正在跑的进程的need_resched置位。
相关文章
|
7月前
|
缓存 负载均衡 Linux
内核:进程与调度机制(笔记)
内核:进程与调度机制(笔记)
141 0
|
Linux 调度 Apache
第十五章、【Linux】例行性工作调度
第十五章、【Linux】例行性工作调度
63 0
|
消息中间件 监控 算法
深入理解Linux进程管理与优化:原理、调度和资源控制详解
深入理解Linux进程管理与优化:原理、调度和资源控制详解
289 0
|
2月前
|
分布式计算 算法 大数据
探索操作系统的核心:调度与内存管理机制
【10月更文挑战第11天】 本文深入探讨了操作系统中两大核心功能——调度与内存管理机制。通过分析调度算法、进程状态转换及内存分配策略等关键方面,揭示了它们如何共同维护系统性能和稳定性。旨在为读者提供对操作系统内部运作的深刻理解,同时引起对优化策略的思考。
75 5
|
3月前
|
安全 Linux 调度
保持内核代码的可抢占安全 【ChatGPT】
保持内核代码的可抢占安全 【ChatGPT】
|
5月前
|
存储 缓存 自然语言处理
(三)JVM成神路之全面详解执行引擎子系统、JIT即时编译原理与分派实现
执行引擎子系统是JVM的重要组成部分之一,在JVM系列的开篇曾提到:JVM是一个架构在平台上的平台,虚拟机是一个相似于“物理机”的概念,与物理机一样,都具备代码执行的能力。
|
7月前
|
算法 调度
【操作系统】处理机调度的基本概念和三个层次、进程调度的时机和方式、调度器、闲逛线程
【操作系统】处理机调度的基本概念和三个层次、进程调度的时机和方式、调度器、闲逛线程
628 3
|
存储 监控 算法
【操作系统】—处理机调度的概念以及层次
【操作系统】—处理机调度的概念以及层次
【操作系统】—处理机调度的概念以及层次
|
算法 调度
【操作系统篇】第五篇——调度(概念,层次,调度时机,切换与过程,方式,评价指标)
【操作系统篇】第五篇——调度(概念,层次,调度时机,切换与过程,方式,评价指标)
【操作系统篇】第五篇——调度(概念,层次,调度时机,切换与过程,方式,评价指标)
|
负载均衡 算法 网络协议
【操作系统】第八章处理机调度
【操作系统】第八章处理机调度
235 0
【操作系统】第八章处理机调度

热门文章

最新文章