【刘蔚然 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

WEEK EIGHT(4.11——4.17)进程的切换和系统的一般执行过程

SECTION 1 进程切换的关键代码switch_to的分析

1.进程调度与进程调度的时机分析

  1. 进程分类

    1. 分类1

      1. I/O-bound:等待I/O
      2. CPU-bound:大量占用CPU进行计算
    2. 分类2
      1. 交互式进程(shell)
      2. 实时进程
      3. 批处理进程
  2. 进程调度策略
    • 一组决定何时以何种方式选择进程的规则
    • Linux的调度基于分时和优先级策略:
      1. 进程根据优先级(系统根据特定算法计算出来)排队;
      2. 这个优先级的值表示如何适当分配CPU;
      3. 调度程序会根据进程的运行周期动态调整优先级;
      4. 比如nice等系统调用,可以手动调整优先级
    • 调度策略本质上是一种算法,这些算法从实现的角度看仅仅是从运行队列中选择一个新进程,选择的过程中运用了不同的策略而已
    • 内核中的调度算法相关代码使用了类似OOD中的策略模式
  3. 进程调度的时机
    • 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule()(也就是说,用户态进程只能被动地调度);
    • 内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
    • 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。

2.进程切换上下文的相关代码

1.概念:

  • 为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换(挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行)
  • 进程上下文包含了进程执行需要的所有信息
    • 用户地址空间:包括程序代码,数据,用户堆栈等
    • 控制信息:进程描述符,内核堆栈等
    • 硬件上下文(中断也要保存硬件上下文只是保存的方法不同,中断是通过压栈来解决的,而这里是通过schedule函数)
  1. 代码

    • schedule()函数选择一个新的进程来运行,下图就是选择函数

    • 下一步通过context_switch完成进程上下文切换

    • switch_to完成寄存器的切换:先保存当前进程的寄存器,再进行堆栈切换(下图第44、45行)自此后所有的压栈都是在新进程的堆栈中了,再切换eip(下图46、56行),这样当前进程可以从新进程中恢复,还有其他必要的切换

    • next_ip一般是$1f(对于新创建的进程来说就是ret_from_fork)

    • 49行jmp __switch_to是函数调用,通过寄存器传递参数;函数执行结束return的时候从下一条指令开始(也就是认为是新进程的开始)

    • 51行,next进程曾经是prev进程(即:对于之前提到的next而言,它执行完后执行的“下一个”其实是刚刚被切换的进程)

        1.31#define switch_to(prev, next, last)                    \
      2.32do { \
      3.33 /* \
      4.34 * Context-switching clobbers all registers, so we clobber \
      5.35 * them explicitly, via unused output variables. \
      6.36 * (EAX and EBP is not listed because EBP is saved/restored \
      7.37 * explicitly for wchan access and EAX is the return value of \
      8.38 * __switch_to()) \
      9.39 */ \
      10.40 unsigned long ebx, ecx, edx, esi, edi; \
      11.41 \
      12.42 asm volatile("pushfl\n\t" /* save flags */ \
      13.43 "pushl %%ebp\n\t" /* save EBP */ \
      14.44 "movl %%esp,%[prev_sp]\n\t" /* save ESP */ \
      15.45 "movl %[next_sp],%%esp\n\t" /* restore ESP */ \
      16.46 "movl $1f,%[prev_ip]\n\t" /* save EIP */ \
      17.47 "pushl %[next_ip]\n\t" /* restore EIP */ \
      18.48 __switch_canary \
      19.49 "jmp __switch_to\n" /* regparm call */ \
      20.50 "1:\t" \
      21.51 "popl %%ebp\n\t" /* restore EBP */ \
      22.52 "popfl\n" /* restore flags */ \
      23.53 \
      24.54 /* output parameters */ \
      25.55 : [prev_sp] "=m" (prev->thread.sp), \
      26.56 [prev_ip] "=m" (prev->thread.ip), \
      27.57 "=a" (last), \
      28.58 \
      29.59 /* clobbered output registers: */ \
      30.60 "=b" (ebx), "=c" (ecx), "=d" (edx), \
      31.61 "=S" (esi), "=D" (edi) \
      32.62 \
      33.63 __switch_canary_oparam \
      34.64 \
      35.65 /* input parameters: */ \
      36.66 : [next_sp] "m" (next->thread.sp), \
      37.67 [next_ip] "m" (next->thread.ip), \
      38.68 \
      39.69 /* regparm parameters for __switch_to(): */ \
      40.70 [prev] "a" (prev), \
      41.71 [next] "d" (next) \
      42.72 \
      43.73 __switch_canary_iparam \
      44.74 \
      45.75 : /* reloaded segment registers */ \
      46.76 "memory"); \
      47.77} while (0)

SECTION 2 Linux系统的一般执行过程

1. Linux系统的一般执行过程分析

最一般的情况:正在运行的用户态进程X切换到运行用户态进程Y的过程

  • 正在运行的用户态进程X
  • 发生中断——
    • save cs:eip/esp/eflags(current) to kernel stack;
    • then load cs:eip(系统调用的起点,entry of a specific ISR) and ss:esp(point to kernel stack)
  • 进入内核代码,SAVE_ALL //保存现场
  • (这一步也可能不发生)中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换(把当前进程X的用户堆栈切换到需要的其他进程堆栈中)
  • 标号1之后开始运行上一步中选中的用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行)
  • restore_all //恢复现场
  • iret - pop cs:eip/ss:esp/eflags from kernel stack
  • 继续运行用户态进程Y
  • 补充:中断上下文和进程长下文切换
    • 前者是CPU内部的切换;后者是在内核中堆栈的切换

3.Linux系统执行过程中的几个特殊情况

  1. 通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断的时候没有进程用户态和内核态的转换,cs不会变化;
  2. 内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,也不需要从中断中返回,与最一般的情况相比更简略;
  3. 创建子进程的系统调用在子进程中的执行起点(如ret_from_fork,上文中也已经提到过)及返回用户态;
  4. 加载一个新的可执行程序后返回到用户态的情况,如execve,在新进程内部修改了中断保存的信息

4.地址切换

  1. 进程的地址空间一共有4G,其中0——3G是用户态可以访问,3G以上只有内核态可以访问
  2. 内核&舞女
    1. 内核就是各种中断处理过程和内核线程的集合;
    2. 内核相当于出租车,可以为每一个“招手”的进程提供内核态到用户态的转换;
    3. 没有进程需要“承载”的时候,内核进入idle0号进程进行“空转”;
    4. 3G以上的部分就是这样的“出租车”,是所有进程共享的,在内核态部分切换的时候就比较容易

SECTION 3 LINUX 系统架构和执行过程概述

1.最简单也是最复杂的操作——ls

2.CPU和内存的角度看Linux系统的执行

  1. 执行gets()函数;

  2. 执行系统调用,陷入内核;

  3. 等待输入,CPU会调度其他进程执行,同时wait一个I/O中断;

  4. 敲击ls,发I/O中断给CPU,中断处理程序进行现场保存、压栈等等;

  5. 中断处理程序发现X进程在等待这个I/O(此时X已经变成阻塞态),处理程序将X设置为WAKE_UP;

  6. 进程管理可能会把进程X设置为next进程,这样gets系统调用获得数据,再返回用户态堆栈

  7. 从内存角度看,所有的物理地址都会被映射到3G以上的地址空间:因为这部分对所有进程来说都是共享的

实验

1.自行调试schedule函数

  1. 进入实验楼虚拟机环境;

  2. 启动内核,并进入调试状态

  3. 在schedule处设置断点,点击c运行。【可以看到此时被冻结的内核开始运行直到schedule处】

  4. list查看断点所在代码段。可以与上图对照,发现第2804行即schedule()函数

  5. c之后按n单步执行,直到遇到__schedule函数,进入其中查看

  6. 继续执行,直到发现context_switch函数

  7. 设置断点后,设法进入其内部查看

疑问与自查

1.什么是OOD?

参考http://baike.baidu.com/link?url=PyEf6WeBbsLzlzXC-icrANr_0Hiw6wa-Y6DHFXn8fJ_ASRVYR_IMGONy51DgmFTDpPTPNUP86dl38zX8ym_s0K

OOD是面向对象设计,OOD的目标是管理程序内部各部分的相互依赖。为了达到这个目标,OOD要求将程序分成块,每个块的规模应该小到可以管理的程度,然后分别将各个块隐藏在接口(interface)的后面,让它们只通过接口相互交流。比如说,如果用OOD的方法来设计一个服务器-客户端(client-server)应用,那么服务器和客户端之间不应该有直接的依赖,而是应该让服务器的接口和客户端的接口相互依赖。

《Linux内核分析》第八周 进程的切换和系统的一般执行过程的更多相关文章

  1. Linux内核分析第八周——进程的切换和系统的一般执行过程

    Linux内核分析第八周--进程的切换和系统的一般执行过程 李雪琦+原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/cou ...

  2. 20135327郭皓--Linux内核分析第八周 进程的切换和系统的一般执行过程

    第八周 进程的切换和系统的一般执行过程 一.进程切换的关键代码switch_to分析 1.进程调度与进程调度的时机分析 不同类型的进程有不同的调度需求 第一种分类: I/O-bound:频繁进行I/O ...

  3. Linux内核设计第八周 ——进程的切换和系统的一般执行过程

    Linux内核设计第八周 ——进程的切换和系统的一般执行过程 第一部分 知识点总结 第二部分 实验部分 1.配置实验环境,确保menu内核可以正常启动 2.进入gdb调试,在shedule和conte ...

  4. 《Linux内核》第七周 进程的切换和系统的一般执行过程 20135311傅冬菁

    进程的切换和系统的一般执行过程 一.内容总结与分析 进程调度与进程调度时机 进程调度需求的分类: 第一种分类方式: I/O -bound(频繁进行I/O,通常会花很多时间等待I/O操作) CPU-bo ...

  5. Linux内核及分析 第八周 进程的切换和系统的一般执行过程

    学习笔记: 一.进程调度与进程调度的时机分析 1.不同类型的进程有不同需求的调度需求: 第一种分类: —I/O-bound:频繁的进行I/O,通常会花费很多时间等待I/O操作的完成 —CPU-boun ...

  6. Linux内核分析——第八周学习笔记20135308

    第八周 进程的切换和系统的一般执行过程 一.进程切换的关键代码switch_to分析 1.进程调度与进程调度的时机分析 (1)进程分类 第一种分类 I/O-bound:等待I/O CPU-bound: ...

  7. 《Linux内核分析》 第八节 进程的切换和一般的执行过程

    张嘉琪 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 Linux内核分析 第八 ...

  8. 20135337朱荟潼 Linux第八周学习总结——进程的切换和系统的一般执行过程

    第八周 进程的切换和系统的一般执行过程 一.进程切换关键代码switch_to 1.不同类型进程有不同调度需求--两种分类 2.调度策略--规则 Linux中进程优先级是动态的,周期性调整. 3.时机 ...

  9. Linux 第八周实验 进程的切换和系统的一般执行过程

    姬梦馨 原创作品 <Linux内核分析>MOOC课程:http://mooc.study.163.com/course/USTC-1000029000 第八讲 进程的切换和系统的一般执行过 ...

随机推荐

  1. .NET LINQ 元素操作

    元素操作      元素操作从一个序列返回单个特定元素. 方法 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 ElementAt 返回集合中指定索引处的元素. ...

  2. 一张图说明CDN网络的原理

    原文: http://blog.csdn.net/coolmeme/article/details/9468743 1.用户向浏览器输入www.web.com这个域名,浏览器第一次发现本地没有dns缓 ...

  3. 获取到Android控件的高度

    问题 如何获取一个控件的长和高,相信很多朋友第一眼看见这个问题都会觉得很简单,直接在onCreate里面调用getWidth.getMeasuredWidth不就可以获得了吗,但是,事实上是并没有简单 ...

  4. Pyqt SpVoice朗读功能

    用Pyqt 做一个读取系统剪贴板内容,然后通过语音合成(TTS)朗读出剪贴板的内容 知识要点 SpVoice SpVoice类是支持语音合成(TTS)的核心类.通过SpVoice对象调用TTS引擎,从 ...

  5. JVM 1.6 GC

    JVM调优是一门艺术. JVM调优的重点是减少Major GC的次数,因为Major GC会暂停程序比较长的时间.如果Major GC的次数比较多,意味着应用程序的JVM内存参数需要调整. JVM内存 ...

  6. Hadoop Linux安装

    Hadoop Linux安装 步骤流程 1.硬件准备 2.软件准备(推荐CDH) 3.将Hadoop安装包分发到各个节点下 4.安装JDK 5.修改/etc/hosts配置文件 6.设置SSH免密码登 ...

  7. springMVC含文件上传调用ajax无法连接后台

    springMVC在使用ajax进行后台传值的时候发现找不到对应的requestMapping(""),无法进入后台,在多次试验后确定是 MultipartFile对象与ajax冲 ...

  8. [软件推荐]VMware Workstation 12.1.1多国语言(含简体中文)+激活方法

    虚拟机VMware功能强大,使用方便,可以在同一台电脑上安装多个系统(Windows.Linux.OS).虚拟机上的所有操作都不会影响到“实体机”,因此在虚拟机中可以进行很多测试操作,如果某些软件使用 ...

  9. hibernate模糊查询

    hibernate模糊查询-Restrictions.ilike & Expression.like Criteria criteria = session.createCriteria(Ta ...

  10. Windows Server 2008 R2 DNS 服务器迁移