第一部分:实验

进程的调度时机与进程的切换

操作系统原理中介绍了大量进程调度算法,这些算法从实现的角度看仅仅是从运行队列中选择一个新进程,选择的过程中运用了不同的策略而已。

对于理解操作系统的工作机制,反而是进程的调度时机与进程的切换机制更为关键。

进程调度的时机

  • 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
  • 内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
  • 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。

进程的切换

  • 为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;
  • 挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;
  • 进程上下文包含了进程执行需要的所有信息
  • 用户地址空间:包括程序代码,数据,用户堆栈等
  • 控制信息:进程描述符,内核堆栈等
  • 硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)
  • schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换
  • next = pick_next_task(rq, prev);//进程调度算法都封装这个函数内部
  • context_switch(rq, prev, next);//进程上下文切换
  • switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程

switch_to函数如下所示:

  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" /* 保存当前进程flags */
  13. 43 "pushl %%ebp\n\t" /* 当前进程堆栈基址压栈*/
  14. 44 "movl %%esp,%[prev_sp]\n\t" /*保存ESP,将当前堆栈栈顶保存起来*/
  15. 45 "movl %[next_sp],%%esp\n\t" /*更新ESP,将下一栈顶保存到ESP中*/
  16. //完成内核堆栈的切换
  17. 46 "movl $1f,%[prev_ip]\n\t" /*保存当前进程EIP*/
  18. 47 "pushl %[next_ip]\n\t" /*将next进程起点压入堆栈,即next进程的栈顶为起点*/
  19. 48
  20. //完成EIP的切换
  21. __switch_canary
  22. //next_ip一般是$1f,对于新创建的子进程时ret_from_fork
  23. 49 "jmp __switch_to\n" /*prev进程中,设置next进程堆栈*/
  24. //jmp不同于call是通过寄存器传递参数
  25. 50 "1:\t" //next进程开始执行
  26. 51 "popl %%ebp\n\t"
  27. 52 "popfl\n"
  28. 53
  29. 54 /*输出变量定义*/
  30. 55 : [prev_sp] "=m" (prev->thread.sp), //[prev_sp]定义内核堆栈栈顶
  31. 56 [prev_ip] "=m" (prev->thread.ip), //[prev_ip]当前进程EIP
  32. 57 "=a" (last),
  33. 58
  34. 59 /* 要破坏的寄存器: */
  35. 60 "=b" (ebx), "=c" (ecx), "=d" (edx),
  36. 61 "=S" (esi), "=D" (edi)
  37. 62
  38. 63 __switch_canary_oparam
  39. 64
  40. 65 /* 输入变量: */
  41. 66 : [next_sp] "m" (next->thread.sp), //[next_sp]下一个内核堆栈栈顶
  42. 67 [next_ip] "m" (next->thread.ip),
  43. 68 //[next_ip]下一个进程执行起点,,一般是$1f,对于新创建的子进程是ret_from_fork
  44. 69 /* regparm parameters for __switch_to(): */
  45. 70 [prev] "a" (prev),
  46. 71 [next] "d" (next)
  47. 72
  48. 73 __switch_canary_iparam
  49. 74
  50. 75 : /* 重新加载段寄存器 */
  51. 76 "memory");
  52. 77} while (0)

使用的还是time命令



test.c函数加的关于time的内容。

  1. #include <time.h>
  2. #include "menu.h"
  3. int Time(int argc, char *argv[])
  4. {
  5. time_t tt;
  6. struct tm *t;
  7. tt = time(NULL);
  8. t = localtime(&tt);
  9. printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
  10. return 0;
  11. }
  12. int TimeAsm(int argc, char *argv[])
  13. {
  14. time_t tt;
  15. struct tm *t;
  16. asm volatile(
  17. "mov $0,%%ebx\n\t"
  18. "mov $0xd,%%eax\n\t"
  19. "int $0x80\n\t"
  20. "mov %%eax,%0\n\t"
  21. : "=m" (tt)
  22. );
  23. t = localtime(&tt);
  24. printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
  25. return 0;
  26. }
  27. MenuConfig("time","Show System Time",Time);
  28. MenuConfig("time-asm","Show System Time(asm)",TimeAsm);

使用gdb跟踪schedule函数

设置断点



按C往下执行,跟踪到schedule(),调用了__schedule()函数。



进入__schedule()函数内部如图所示



往下执行可以看到找下一个要运行的进程。



__schedule()函数内部有一句判断是否需要进行上下文切换的语句。context_switch()是进程上下文切换调用的函数。



但是再往下就执行不下去了。

Linux系统的一般执行过程

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

1.正在运行的用户态进程X

2.发生中断——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).

3.SAVE_ALL //保存现场

4.中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换

5.标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行)

6.restore_all //恢复现场

7.iret - pop cs:eip/ss:esp/eflags from kernel stack

8.继续运行用户态进程Y

几种特殊情况

  • 通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;
  • 内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;
  • 创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork;
  • 加载一个新的可执行程序后返回到用户态的情况,如execve;

第二部分:教材

进程地址空间

进程地址空间是指用户空间中进程的内存,是每个用户空间进程所看到的内存。Linux采用虚拟内存技术,进程之间以虚拟的方式共享内存,每个进程好像都可以访问整个系统的所有物理内存。

进程地址空间由进程可寻址的虚拟内存组成。可以被访问的合法地址空间称为内存区域,进程只能访问有效内存区域内的内存地址。访问了不在有效范围内的内存区域或者以不正确的方式访问了有效地址的进程会被内存终止,并返回“段错误”信息。

内存区域包含各种内存对象:

  • 代码段,即可执行文件代码的内存映射。
  • 数据段,即可执行文件的已初始化全局变量的内存映射。
  • 包含未初始化全局变量即bss段的零页的内存映射。
  • 用于进程用户空间栈的零页的内存映射。
  • 每一个诸如C库或动态链接程序等共享库的代码段、数据段和bss段也会被载入进程的地址空间。
  • 任何内存映射文件。
  • 任何共享内存段。
  • 任何匿名的内存映射,如malloc分配的内存。

页高速缓存和页回写

写缓存有三种策略:

  • 不缓存,高速缓存不缓存任何写操作,跳过缓存写到磁盘上,同时使缓存中数据失效。
  • 写透缓存,自动更新内存缓存,同时也更新磁盘文件。
  • “回写”,写操作直接写到缓存中,后端存储不会立刻直接更新,将页高速缓存中被写入的页面标记成“脏”,由回写进程将脏页写回磁盘。(Flusher内核线程负责)

20169210《Linux内核原理与分析》第十周作业的更多相关文章

  1. 20169210《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...

  2. 20169212《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...

  3. 2018-2019-1 20189221 《Linux内核原理与分析》第九周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第九周作业 实验八 理理解进程调度时机跟踪分析进程调度与进程切换的过程 进程调度 进度调度时机: 1.中断处理过程(包 ...

  4. 2017-2018-1 20179215《Linux内核原理与分析》第二周作业

    20179215<Linux内核原理与分析>第二周作业 这一周主要了解了计算机是如何工作的,包括现在存储程序计算机的工作模型.X86汇编指令包括几种内存地址的寻址方式和push.pop.c ...

  5. 2019-2020-1 20199329《Linux内核原理与分析》第九周作业

    <Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...

  6. 2019-2020-1 20199329《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...

  7. 2019-2020-1 20209313《Linux内核原理与分析》第二周作业

    2019-2020-1 20209313<Linux内核原理与分析>第二周作业 零.总结 阐明自己对"计算机是如何工作的"理解. 一.myod 步骤 复习c文件处理内容 ...

  8. 2018-2019-1 20189221《Linux内核原理与分析》第一周作业

    Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...

  9. 《Linux内核原理与分析》第一周作业 20189210

    实验一 Linux系统简介 这一节主要学习了Linux的历史,Linux有关的重要人物以及学习Linux的方法,Linux和Windows的区别.其中学到了LInux中的应用程序大都为开源自由的软件, ...

  10. 2018-2019-1 20189221《Linux内核原理与分析》第二周作业

    读书报告 <庖丁解牛Linux内核分析> 第 1 章 计算工作原理 1.1 存储程序计算机工作模型 1.2 x86-32汇编基础 1.3汇编一个简单的C语言程序并分析其汇编指令执行过程 因 ...

随机推荐

  1. (转载).Net HttpPost的发送和接收示例代码

    HttpPost在不同系统进行数据交互的时候经常被使用.它的最大好处在于直接,不像Webservice或者WCF需要wsdl作为一个双方的"中介".在安全性上,往往通过IP限制的方 ...

  2. 离开ACM了,总结一下

    写这篇博客,一如当初我对着电脑显示器,不知道从哪里下手才是,所以没准写着写着就出现了倒叙插叙补叙等充满语文功底的修辞手法,不过不会有45度的妩媚和忧伤. 像一位程序员所说:今天的努力是为了儿时吹过的牛 ...

  3. bzoj3677: [Apio2014]连珠线

    Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色.游戏 开始时,只有1个珠子,而接下来新的 ...

  4. Quartz1.8.5例子(十一)

    /* * Copyright 2005 - 2009 Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the ...

  5. android SurfaceView绘制 重新学习--控制动画移动

    直接上demo,图是自己切的,将就用吧.点击左右两边分别向左右移动. public class MySurfaceView extends SurfaceView implements Callbac ...

  6. css3媒体查询判断移动设备横竖屏

    /* 设备竖屏时调用该段css代码 */ @media all and (orientation : portrait){ body{   background-color:blue;  } } /* ...

  7. maven 根据不同的环境打war包-->资源文件的处理方式

    发现犯的错误: 1. 指定了testResource 文件夹与resource 为同一个文件夹.导致不论在resource 里面如何过滤文件,都不起作用.资源文件本来就是共享的.不必这样指定. 2. ...

  8. awsomeplayer结构认识

    把这个搞明白,算是顿悟的一个真实例子.怎么也搞不懂的架构,突然就想明白了.不过这其实是一个思维的过程. 当然如果你想明白这些东西,至少要非常清楚一个概念:接口. 我只是一个半路出家的开发者,我真正明白 ...

  9. USB otg 学习笔记

    1 USB OTG的工作原理 OTG补充规范对USB2.0的最重要的扩展是其更具节能性的电源管理和允许设备以主机和外设两种形式工作.OTG有两种设备类型:两用OTG设备(Dualrole device ...

  10. webview调用javascript脚本无反应

    最近遇到一个问题:在html中有一段javascript脚本定义了一个方法,在使用webview.loadUrl("javascript:方法名()")时方法未执行,后来 查资料发 ...