内核代码阅读(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置位。
相关文章
|
5月前
|
缓存 负载均衡 Linux
内核:进程与调度机制(笔记)
内核:进程与调度机制(笔记)
116 0
|
消息中间件 监控 算法
深入理解Linux进程管理与优化:原理、调度和资源控制详解
深入理解Linux进程管理与优化:原理、调度和资源控制详解
257 0
|
4月前
|
机器学习/深度学习 算法 安全
探索现代操作系统的内核设计与优化
在当今数字化时代,操作系统的内核是计算机系统稳定、高效运行的关键。本文深入探讨了现代操作系统内核的设计原则和优化方法,从微内核到宏内核,详细分析了它们各自的优缺点,并探讨了未来内核的发展趋势和创新方向。
69 1
|
21天前
|
存储 监控 安全
探究Linux操作系统的进程管理机制及其优化策略
本文旨在深入探讨Linux操作系统中的进程管理机制,包括进程调度、内存管理以及I/O管理等核心内容。通过对这些关键组件的分析,我们将揭示它们如何共同工作以提供稳定、高效的计算环境,并讨论可能的优化策略。
23 0
|
5月前
|
算法 调度
【操作系统】处理机调度的基本概念和三个层次、进程调度的时机和方式、调度器、闲逛线程
【操作系统】处理机调度的基本概念和三个层次、进程调度的时机和方式、调度器、闲逛线程
402 3
|
10月前
|
存储 监控 算法
【操作系统】—处理机调度的概念以及层次
【操作系统】—处理机调度的概念以及层次
【操作系统】—处理机调度的概念以及层次
|
算法 调度
【操作系统篇】第五篇——调度(概念,层次,调度时机,切换与过程,方式,评价指标)
【操作系统篇】第五篇——调度(概念,层次,调度时机,切换与过程,方式,评价指标)
【操作系统篇】第五篇——调度(概念,层次,调度时机,切换与过程,方式,评价指标)
|
负载均衡 算法 网络协议
【操作系统】第八章处理机调度
【操作系统】第八章处理机调度
214 0
【操作系统】第八章处理机调度
|
监控 Windows
驱动开发:内核监控进程与线程创建
监控进程的启动与退出可以使用 `PsSetCreateProcessNotifyRoutineEx` 来创建回调,当新进程产生时,回调函数会被率先执行,然后执行我们自己的`MyCreateProcessNotifyEx`函数,并在内部进行打印输出。
519 0
驱动开发:内核监控进程与线程创建
驱动开发:内核中枚举进线程与模块
内核枚举进程使用`PspCidTable` 这个未公开的函数,它能最大的好处是能得到进程的EPROCESS地址,由于是未公开的函数,所以我们需要变相的调用这个函数,通过`PsLookupProcessByProcessId`函数查到进程的EPROCESS,如果`PsLookupProcessByProcessId`返回失败,则证明此进程不存在,如果返回成功则把EPROCESS、PID、PPID、进程名等通过DbgPrint打印到屏幕上。
454 0
驱动开发:内核中枚举进线程与模块