从系统调用返回到用户空间是否调度,从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. }


linux内核情景分析之强制性调度的更多相关文章

  1. linux内核情景分析之execve()

    用来描述用户态的cpu寄存器在内核栈中保存情况.可以获取用户空间的信息 struct pt_regs { long ebx; //可执行文件路径的指针(regs.ebx中 long ecx; //命令 ...

  2. Linux内核情景分析之消息队列

    早期的Unix通信只有管道与信号,管道的缺点: 所载送的信息是无格式的字节流,不知道分界线在哪,也没通信规范,另外缺乏控制手段,比如保温优先级,管道机制的大小只有1页,管道很容易写满而读取没有及时,发 ...

  3. Linux内核情景分析的alloc_pages

    NUMA结构的alloc_pages ==================== mm/numa.c 43 43 ==================== 43 #ifdef CONFIG_DISCON ...

  4. linux内核情景分析之exit与Wait

    //第一层系统调用 asmlinkage long sys_exit(int error_code) { do_exit((error_code&0xff)<<8); } 其主体是 ...

  5. linux内核情景分析之内核中的互斥操作

    信号量机制: struct sempahore是其结构,定义如下 struct semaphore { atomic_t count;//资源数目 int sleepers;//等待进程数目 wait ...

  6. Linux内核情景分析之异常访问,用户堆栈的扩展

    情景假设: 在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存.也就是所说的野指针访问. 当cpu产生页面错误时,会把失败的线性地址放在cr2寄存器.线性地址缺页异常的4种情况 1.如果 ...

  7. linux内核情景分析之命名管道

    管道是一种"无名","无形文件,只可以近亲进程使用,不可以再任意两个进程通信使用,所以只能实现"有名","有形"的文件来实现就可以 ...

  8. linux内核情景分析之信号实现

    信号在进程间通信是异步的,每个进程的task_struct结构有一个sig指针,指向一个signal_struct结构 定义如下 struct signal_struct { atomic_t cou ...

  9. linux内核情景分析之匿名管道

    管道的机制由pipe()创建,由pipe()所建立的管道两端都在同一进程.所以必须在fork的配合下,才可以在具有亲缘关系的进程通信 /* * sys_pipe() is the normal C c ...

随机推荐

  1. static关键字 详解

    原文地址:http://blog.csdn.net/keyeagle/article/details/6708077 google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇 ...

  2. UVA - 10213 How Many Pieces of Land?(欧拉公式 + 高精度)

    圆上有n个点,位置不确定.问这些点两两连接成的线段,最多可以把圆划分成多少块平面? 欧拉公式:V-E+F = 2,V是点数,E是边数,F是面数. 答案是F=C(n,4)+C(n,2)+1,看的别人推的 ...

  3. 笔记-python-lib-内置函数

    笔记-python-lib-内置函数 注:文档来源为Python3.6.4官方文档 1.      built-in functions abs(x) 返回绝对值 all(iterable)   re ...

  4. Mysql存储过程中的事务回滚

    create procedure test(in a int) BEGIN ; ;-- 异常时设置为1 START TRANSACTION; ,); ,); THEN ROLLBACK; ELSE C ...

  5. python 闯关之路四(下)(并发编程与数据库编程) 并发编程重点

    python 闯关之路四(下)(并发编程与数据库编程)   并发编程重点: 1 2 3 4 5 6 7 并发编程:线程.进程.队列.IO多路模型   操作系统工作原理介绍.线程.进程演化史.特点.区别 ...

  6. CMD 下运行python的unittest测试脚本无输出

    正常情况下windows的命令行执行python脚本命令: python 脚本名.py 我这样做了,看截图可以看到,并没有期待中那样有一堆高大上的信息输出,反而毛都没有!!!! 于是,我想起了度娘,但 ...

  7. Spring boot 上传文件大小限制

    1.spring boot 1.x 版本 application.properties 文件中 位置在(resources下) spring.http.multipart.maxFileSize = ...

  8. hnust CZJ-Superman

    问题 B: CZJ-Superman 时间限制: 1 Sec  内存限制: 128 MB提交: 636  解决: 87[提交][状态][讨论版] 题目描述 “那是只鸟?那是飞机?那是——超人!” 程序 ...

  9. 微信Oauth2.0网页开放授权

    网页授权获取用户基本信息 如果用户在微信中(Web微信除外)访问公众号的第三方网页,公众号开发者可以通过此接口获取当前用户基本信息(包括昵称.性别.城市.国家).利用用户信息,可以实现体验优化.用户来 ...

  10. File IO(NIO.2):什么是路径?

    简介 文件系统以某种形式的媒体(通常为一个或多个硬盘驱动器)存储和组织文件,使得它们可以容易地被检索.目前使用的大多数文件系统将文件存储在树形(或分层)结构中.在树的顶部是一个(或多个)根节点.在根节 ...