第一部分:实验

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

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

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

进程调度的时机

  • 中断处理过程(包括时钟中断、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. mongodb篇二:mongodb克隆远程数据库,去重查询的命令及对应java语句

    http://blog.csdn.net/qkxh320/article/details/16115671 1.首先操作mongodb最基本命令:: show databases;           ...

  2. vs2012生成的项目,如何在只装有VS2010的电脑上打开

    步骤: 1.用记事本打开Vs2012生成的项目解决方案文件(.sln文件)文件 2.修改前两行 Microsoft Visual Studio Solution File, Format Versio ...

  3. sql性能优化总结(转)

    网上看到一篇sql优化的文章,整理了一下,发现很不错,虽然知道其中的部分,但是没有这么全面的总结分析过…… 一.   目的 数据库参数进行优化所获得的性能提升全部加起来只占数据库应用系统性能提升的40 ...

  4. Adding the Test API in The ASP.NET Web API Help Page

    1.通过NuGet引用Web API Test Client 引用玩该DLL会生成如下文件: 这里面就是我们的帮助文档界面 2.在项目属性中进行如下设置,勾选XMl文档文件,并设置路径 3.在项目的A ...

  5. C技巧:结构体参数转成不定参数

    下面这段程序是一个C语言的小技巧,其展示了如何把一个参数为结构体的函数转成一个可变参数的函数,其中用到了宏和内建宏"__VA_ARGS__",下面这段程序可以在GCC下正常编译通过 ...

  6. ubuntu修改grub2

    转自修改系统启动项 grub2配置的方法 ubuntu 在早期的Ubuntu中,使用Grub作为系统的启动引导程序,想修改系统启动项非常简单,只要用gedit打开系统菜单设定文件( sudo gedi ...

  7. Z-stack之OSAL初始化流程

    转自点击打开链接 我使用的协议栈版本及例子信息: ZigBee2006\Texas Instruments\ZStack-1.4.3-1.2.1\Projects\zstack\Samples\Sam ...

  8. 驱动程序环境搭配(VS2010 + WDK + VirtualDDK + VMware)good

    http://www.cnblogs.com/BoyXiao/archive/2011/07/31/2122755.html

  9. 解读30个提高Web程序执行效率的好经验

    其实微博是个好东西,关注一些技术博主之后,你不用再逛好多论坛了,因为一些很好的文章微博会告诉你,最近看到酷勤网推荐的一篇文章<30个提高Web程序执行效率的好经验>,文章写得不错,提到一些 ...

  10. unicode下各种类型转换CString、string

    把最近用到的各种unicode下类型转换总结了一下: 1.string转CString string a=”abc”; CString str=CString(a.c_str()); 或str.for ...