zoukankan      html  css  js  c++  java
  • linux内核情景分析之强制性调度

    从系统调用返回到用户空间是否调度,从ret_with_reschedule可看出,是否真正调度,取决于当前进程的pcb中的need_resched是否设置为1,那如何设置为1取决于以下几种情况:
    时间中断处理程序,发现当前进程运行时间过长:每次发生时间中断,都要递减该进程的时间片,一旦count为0,强制调度,剥夺当前进程运行
    1. void update_process_times(int user_tick)
    2. {
    3. struct task_struct *p = current;
    4. int cpu = smp_processor_id(), system = user_tick ^ 1;
    5. update_one_process(p, user_tick, system, cpu);//统计信息而已
    6. if (p->pid) {
    7. if (--p->counter <= 0) {
    8. p->counter = 0;
    9. p->need_resched = 1;//强制调度
    10. }
    11. if (p->nice > 0)
    12. kstat.per_cpu_nice[cpu] += user_tick;
    13. else
    14. kstat.per_cpu_user[cpu] += user_tick;
    15. kstat.per_cpu_system[cpu] += system;
    16. } else if (local_bh_count(cpu) || local_irq_count(cpu) > 1)
    17. kstat.per_cpu_system[cpu] += system;
    18. }
     如果此时在用户态发生中断,进入内核态,p->counter减为0,那么p->need_resched就置为1,中断返回时就会强制调度。

        如果此时发生系统调用,进入内核态,再发生中断,p->counter减为0,那么p->need_resched就置为1,中断返回后,然后系统调用返回时就会强制调度。

        如果此时在用户态发生异常,进入内核态,再发生中断,p->counter减为0,那么p->need_resched就置为1,中断返回后,然后异常返回时就会强制调度。


    第二种情况
    唤醒一个睡眠进程,发现被唤醒的进程比当前进程权值高,need_sched设置为1
    1. /*
    2. * Wake up a process. Put it on the run-queue if it's not
    3. * already there. The "current" process is always on the
    4. * run-queue (except when the actual re-schedule is in
    5. * progress), and as such you're allowed to do the simpler
    6. * "current->state = TASK_RUNNING" to mark yourself runnable
    7. * without the overhead of this.
    8. */
    9. inline void wake_up_process(struct task_struct * p)
    10. {
    11. unsigned long flags;
    12. /*
    13. * We want the common case fall through straight, thus the goto.
    14. */
    15. spin_lock_irqsave(&runqueue_lock, flags);
    16. p->state = TASK_RUNNING;//设置为可执行状态
    17. if (task_on_runqueue(p))//如果已经到run队列
    18. goto out;
    19. add_to_runqueue(p);//加入run队列
    20. reschedule_idle(p);//将唤醒进程与当前进程比较,如果唤醒进程比当前进程权值高,那就把当前进程的need_resched设置为1
    21. out:
    22. spin_unlock_irqrestore(&runqueue_lock, flags);
    23. }

    1. static void reschedule_idle(struct task_struct * p)
    2. {
    3. ......
    4. int this_cpu = smp_processor_id();
    5. struct task_struct *tsk;
    6. tsk = cpu_curr(this_cpu);//获取当前进程的task_struct数据结构
    7. if (preemption_goodness(tsk, p, this_cpu) > 1)//比较当前进程和被唤醒的进程的综合权值
    8. tsk->need_resched = 1;//如果被唤醒的进程的综合权值比当前进程的大,那么强制调度
    9. }

     对于第三种情况,实际上应被视为自愿的让出。但是,从内核代码的形式上看,也是通过相同的办法,将当前进程的need_resched标志置为1,使得在进程返回用户空间前夕发生调度,所以也放在这一节。此类系统调用有两个,一个是sched_setscheduler(),另一个是sched_yield()。

        系统调用sched_setscheduler()的作用是改变进程的调度政策。用户登录到系统后,第一个进程的适用调度政策为SCHED_OTHER,也就是默认为无实时要求的交互式应用。在fork()创建新进程时则将此进程适用的调度政策遗传给了子进程。但是,用户可以通过系统调用sched_setscheduler()改变其适用调度政策。

    sched_setscheduler,内核态对应的代码如下:


    1. asmlinkage long sys_sched_setscheduler(pid_t pid, int policy,
    2. struct sched_param *param)
    3. {
    4. return setscheduler(pid, policy, param);
    5. }
    6. asmlinkage long sys_sched_setparam(pid_t pid, struct sched_param *param)
    7. {
    8. return setscheduler(pid, -1, param);
    9. }

    1. static int setscheduler(pid_t pid, int policy,
    2. struct sched_param *param)
    3. {
    4. struct sched_param lp;
    5. struct task_struct *p;
    6. int retval;
    7. retval = -EINVAL;
    8. if (!param || pid < 0)
    9. goto out_nounlock;
    10. retval = -EFAULT;
    11. if (copy_from_user(&lp, param, sizeof(struct sched_param)))//从用户空间把sched_param结构拷贝到lp
    12. goto out_nounlock;
    13. /*
    14. * We play safe to avoid deadlocks.
    15. */
    16. read_lock_irq(&tasklist_lock);
    17. spin_lock(&runqueue_lock);
    18. p = find_process_by_pid(pid);//通过pid找到task_struct
    19. retval = -ESRCH;
    20. if (!p)
    21. goto out_unlock;
    22. if (policy < 0)//policy为-1
    23. policy = p->policy;//维持原来的政策
    24. else {
    25. retval = -EINVAL;
    26. if (policy != SCHED_FIFO && policy != SCHED_RR &&
    27. policy != SCHED_OTHER)//必须是这三种政策之一
    28. goto out_unlock;
    29. }
    30. /*
    31. * Valid priorities for SCHED_FIFO and SCHED_RR are 1..99, valid
    32. * priority for SCHED_OTHER is 0.
    33. */
    34. retval = -EINVAL;
    35. if (lp.sched_priority < 0 || lp.sched_priority > 99)//实时进程的priority必须处于0-99
    36. goto out_unlock;
    37. if ((policy == SCHED_OTHER) != (lp.sched_priority == 0))//如果政策是SCHED_OTHER,sched_priority必须是0
    38. goto out_unlock;
    39. retval = -EPERM;
    40. if ((policy == SCHED_FIFO || policy == SCHED_RR) &&
    41. !capable(CAP_SYS_NICE))
    42. goto out_unlock;
    43. if ((current->euid != p->euid) && (current->euid != p->uid) &&
    44. !capable(CAP_SYS_NICE))
    45. goto out_unlock;
    46. retval = 0;
    47. p->policy = policy;
    48. p->rt_priority = lp.sched_priority;
    49. if (task_on_runqueue(p))
    50. move_first_runqueue(p);//从可执行进程队列的当前位置移到队列的前部,使其在调度时处于较为有利的地位
    51. current->need_resched = 1;//强制调度
    52. out_unlock:
    53. spin_unlock(&runqueue_lock);
    54. read_unlock_irq(&tasklist_lock);
    55. out_nounlock:
    56. return retval;
    57. }


      另一个系统调用sched_yield(),使运行中的进程可以为其他进程"让路",但并不进入睡眠。内核的实现sys_sched_yield,代码如下:
    1. asmlinkage long sys_sched_yield(void)
    2. {
    3. /*
    4. * Trick. sched_yield() first counts the number of truly
    5. * 'pending' runnable processes, then returns if it's
    6. * only the current processes. (This test does not have
    7. * to be atomic.) In threaded applications this optimization
    8. * gets triggered quite often.
    9. */
    10. int nr_pending = nr_running;
    11. #if CONFIG_SMP
    12. int i;
    13. // Substract non-idle processes running on other CPUs.
    14. for (i = 0; i < smp_num_cpus; i++)
    15. if (aligned_data[i].schedule_data.curr != idle_task(i))
    16. nr_pending--;
    17. #else
    18. // on UP this process is on the runqueue as well
    19. nr_pending--;
    20. #endif
    21. if (nr_pending) {//正在等待的运行的进程数
    22. /*
    23. * This process can only be rescheduled by us,
    24. * so this is safe without any locking.
    25. */
    26. if (current->policy == SCHED_OTHER)//当前进程调度策略为sched_other
    27. current->policy |= SCHED_YIELD;//SCHED_YIELD标志位置1,在_schedule_tail清0
    28. current->need_resched = 1;//强制调度
    29. }
    30. return 0;
    31. }






  • 相关阅读:
    fastjson对象,JSON,字符串,map之间的互转
    bootstrap的页面刷新以及模态框的清空
    change 和 propertychange 事件监听input 并发起ajax请求
    jquery 获取和设置select的option值
    Mybatis中使用@Select注解进行模糊查询,使用concat关键字
    mysql 获取表字段及注释
    SpringBoot 在IDEA中实现热部署
    jquery与css控制元素的隐藏和显示的几种方法
    Java8 stream的详细用法
    FATAL ERROR: Could not find ./bin/my_print_defaults 解决方法
  • 原文地址:https://www.cnblogs.com/zengyiwen/p/fe7d32909550362847e78f244bb913f4.html
Copyright © 2011-2022 走看看