《Linux内核原理与设计》第九周作业

视频学习及代码分析

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

不同类型的进程有不同的调度需求,第一种分类:I/O-bound 会频繁的进程I/O,通常会花费很多时间等待I/O操作的完成; CPU-bound 是计算密集型,需要大量的CPU时间进行运算,使得其他交互式进程反应迟钝,因此需要不同的算法来使系统的运行更高效,以及CPU的资源最大限度的得到使用。第二种分类包括批处理进程(batch process);实时进程(real-time process)以及交互式进程(interactive process)。不同的分类需要不同的调度策略,即决定什么时候以怎样的方式选择一个新进程运行。Linux的调度基于分时和优先级。根据优先级排队,且优先级是动态的。

进程调度的时机
  • 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),目的是在运行队列中找到一个进程,把CPU分配给它,或者返回用户态时根据need_resched标记调用schedule(),用户态进程只能被动调度;
  • 内核线程是只有内核态没有用户态的特殊进程,它可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度。内核线程在执行过程中可能中断可以发生时钟中断,I/O中断,但不会发生系统调用,因为它可以直接访问内核的函数;
  • 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
    进程的切换及相关代码分析
  • 为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;
  • 挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;
  • 进程上下文包含了进程执行需要的所有信息

      用户地址空间:包括程序代码,数据,用户堆栈等

      控制信息:进程描述符,内核堆栈等

      硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同,通过保存现场和恢复现场)

  • `schedule()`函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用`switch_to` 来进行关键上下文切换

    `next = pick_next_task(rq, prev); ` //包装了某种进程调度策略

    `context_switch(rq, prev, next); ` //进程上下文切换

    `switch_to(pre,next,prev)` //切换寄存器的状态和堆栈。利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程

    相关代码分析:

    #define switch_to(prev, next, last) //prev指向当前进程,next指向被调度的进程
    do { unsigned long ebx, ecx, edx, esi, edi; asm volatile("pushfl\n\t" //保存当前近程的flags
    "pushl %%ebp\n\t" //把当前进程的基址压栈 "movl %%esp,%[prev_sp]\n\t" //把当前进程的栈顶esp保存到thread.sp中
    "movl %[next_sp],%%esp\n\t" //把[next_sp]放到esp,从而这两步完成了内核堆栈的切换 "movl $1f,%[prev_ip]\n\t" //把1f放到[prev_ip]里,保存当前进程的EIP,当恢复prev进程时可从这里恢复
    "pushl %[next_ip]\n\t" //把next进程的起点,即ip的位置压到堆栈中,next_ip一般是$1f
    __switch_canary
    "jmp __switch_to\n" //执行__switch_to()函数,通过寄存器[prev][next],eax和edx传递参数
    "1:\t"
    "popl %%ebp\n\t"
    "popfl\n" /* output parameters */
    : [prev_sp] "=m"(prev->thread.sp), //为了可读性更好,用字符串[prev_sp]标记参数
    [prev_ip] "=m"(prev->thread.ip),
    "=a" (last), /* clobbered output registers: */
    "=b" (ebx), "=c"(ecx), "=d" (edx),
    "=S" (esi), "=D"(edi) __switch_canary_oparam /* input parameters: */
    : [next_sp] "m" (next->thread.sp),
    [next_ip] "m" (next->thread.ip), /* regparm parameters for __switch_to():*/
    [prev] "a" (prev),
    [next] "d" (next) __switch_canary_iparam : /* reloaded segment registers */
    "memory");
    } while (0)

    二、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). //CPU自动完成保存和加载

    3.SAVE_ALL //保存现场

    4. 中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换 //将x进程的内核堆栈切换到next进程的内核堆栈,再切换eip

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

    6.restore_all //恢复现场

    7.iret - pop cs:eip/ss:esp/eflags from kernel stack //把y进程在发生中断时保存在内核堆栈里面的cs:eip/ss:esp/eflags pop出来

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

    几种特殊情况

  • 通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;
  • 用户态进程不能主动调度schedule(),但内核线程可以主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略 ;
  • 创建子进程的系统调用在子进程中的执行起点及返回用户态,那么不从标号1开始执行,从用户态开始执行,next ip=ret from work,如fork ;
  • 加载一个新的可执行程序后返回到用户态的情况,如execve; //修改了中断上下文

    内核与舞女

    0到3G用户可以访问,3G以上只有内核态可以访问。实际上所有的进程3G以上都是完全共享的,比如进程X切换到进程Y,但是地址空间还是3G以上的部分,只是把进程描述符和其他的进程上下文切换了,等到返回到用户态了才会有不同,在内核态里不管哪个进程代码段和堆栈段都是完全相同的,因此在内核中切换时比较容易的。视频中有一个比喻,就是内核和舞女。内核就好比是出租车,进程是舞女,哪个进程招手都可以进入内核,走一段可以返回到用户态。内核没有进程时就进入0号进程idle空转,有进程时发生中断进入内核态。

    Linux操作系统架构概览

    任何计算机系统都包含一个基本的程序集合,称为操作系统。

    • 内核(进程管理,进程调度,进程间通讯机制,内存管理,中断异常处理,文件系统,I/O系统,网络部分)
    • 其他程序(例如函数库、shell程序、系统程序等等)

    操作系统的目的

    • 与硬件交互,管理所有的硬件资源
    • 为用户程序(应用程序)提供一个良好的执行环境

    最简单也是最复杂的操作-执行ls命令

    从CPU角度看Linux系统的执行

    从内存角度看Linux系统

    参考:图片链接

    教材15、16章学习

    1、进程地址空间由进程可寻址的虚拟地址组成,而且内核允许进程使用这种虚拟内存中的地址。通常情况下,每个进程都有唯一的这种平坦地址空间。进程地址空间中的任何有效地址都只能位于唯一的区域,这些内存区域不能相互覆盖。

    2、一个进程的地址空间与另一个进程的地址空间即使有相同的内存地址,实际上也彼此互不相干,称这样的进程为线程。其父进程希望和其子进程共享空间,可以在调用clone()时,设置CLONE_VM标志;

    3、进程的内核区域包含各种内存对象,比如:可执行文件代码可以包含各种内存映射,称为代码段(text section);可执行文件的已初始化全局变量的内存映射,称为数据段(data section);包含未初始化全局变量,也就是bss段的零页(页面中的信息全部为0值,所以可以用于映射bss段等目的)的内存映射;用于进程用户空间栈的内存映射;

    4、内核使用内存描述符结构体表示进程的地址空间,该结构体包含了和进程地址空间有关的全部信息。内存描述符由mm_struct结构体表示。分配内存描述符时,fork()函数利用copy_mm()函数复制父进程的内存描述符。撤销内存描述符,内核会调用定义在kernel/exit.c中的exit_mm()函数;内核线程没有进程地址空间,也没有相关的内存描述符,所有内核线程对应的进程描述符中的mm域为空。

    5、内存区域由vm_area_struct结构体描述,定义在文件<linux/mm_types.h>中。内存区域在Linux中也经常称作虚拟地址空间。VMA标志是一种位标志,其定义见<linux/mm.h>中,它包含在mm_flags域内,标志了内存区域所包含的页面的行为和信息。

    6、可以通过内存描述符中的mmap和mm_rb域之一访问内存区域。这两个域各自独立地指向与内存描述符相关的全体内存区域对象。其实,它们包含完全相同的am_area_struct结构体的指针,仅仅组织方法不同。mmap域使用单独链表链接所有的内存区域对象;mm_rb域使用红-黑数链接所有的内存区域对象。

    7、为了找到一个给定的内存地址属于哪一个内存区域,内核提供了find_vma()函数。

    8、do_mmap()函数会将一个地址空间加入到进程的地址空间中——无论是扩展已存在的内存区域还是创建一个新的区域; do_munmap()函数从特定的进程空间中删除地址空间。

    9、当程序访问一个虚拟地址时,首先必须将虚拟地址转换为物理地址,然后处理器才能解析地址访问请求。地址的转换需要通过查询页表才能完成,概括的讲,地址转换需要将虚拟地址分段,使每段虚拟地址都作为一个索引指向页表,而页表则指向下一级别的页表或者指向最终的物理页面。Linux中使用三级页表完成转换。

    10、页高速缓存(cache)是Linux内核实现磁盘缓存。写缓存一般被实现成下面三种策略之一:第一种策略称为不缓存(nowrite);第二种策略,写操作将自动更新内存缓存,同时也更新磁盘文件。第三种策略,也是Linux所采用的,称为“回写"。在这种策略下,程序写操作直接写到缓存中,并且被加入到脏页链表中。然后由一个进程(回写进程)周期性将脏页链表中的页写回到磁盘,从而让磁盘中的数据和内存中的最终一致。

    11、页高速缓存通过两个参数address_space兑现加上一个偏移量进行搜索。每个address_space对象都有惟一的基数,它保存在address_space结构体中。基数是一个二叉树,只要指定了文件偏移量,就可以在基数中迅速检索到希望的页。

    2017-2018-1 20179205《Linux内核原理与设计》第九周作业的更多相关文章

    1. 2019-2020-1 20199303<Linux内核原理与分析>第二周作业

      2019-2020-1 20199303第二周作业 1.汇编与寄存器的学习 寄存器是中央处理器内的组成部份.寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令.数据和位址.在中央处理器的控制部件中 ...

    2. 2019-2020-1 20199303 《Linux内核原理分析》 第一周作业

      2019-2020-1 20199303 <Linux内核原理分析> 第一周作业 1. 环境准备 在众多的Linux发行版中,Ubuntu,小红帽还有类Unix系统的BSD系统,我选择了目 ...

    3. 20169219 linux内核原理与分析第二周作业

      "linux内核分析"的第一讲主要讲了计算机的体系结构,和各寄存器之间对数据的处理过程. 通用寄存器 AX:累加器 BX:基地址寄存器 CX:计数寄存器 DX:数据寄存器 BP:堆 ...

    4. 2019-2020-1 20199314 <Linux内核原理与分析>第二周作业

      1.基础学习内容 1.1 冯诺依曼体系结构 计算机由控制器.运算器.存储器.输入设备.输出设备五部分组成. 1.1.1 冯诺依曼计算机特点 (1)采用存储程序方式,指令和数据不加区别混合存储在同一个存 ...

    5. Linux内核原理与分析-第一周作业

      本科期间,学校开设过linux相关的课程,当时的学习方式主要以课堂听授为主.虽然老师也提供了相关的学习教材跟参考材料,但是整体学下来感觉收获并不是太大,现在回想起来,主要还是由于自己课下没有及时动手实 ...

    6. 2019-2020-1 20199314 <Linux内核原理与分析>第一周作业

      前言 本周对实验楼的Linux基础入门进行了学习,目前学习到实验九完成到挑战二. 学习和实验内容 快速学习了Linux系统的发展历程及其简介,学习了下的变量.用户权限管理.文件打包及压缩.常用命令的和 ...

    7. Linux内核原理与分析-第二周作业

      写之前回看了一遍秒速五厘米:如果

    8. 2017-2018-1 20179205《Linux内核原理与设计》第十周作业

      <Linux内核原理与设计>第十周作业 教材17.19.20章学习及收获 1.在Linux以及所有unix系统中,设备被分为以下三种:块设备(blkdev)以块为单位寻址,通过块设备节点来 ...

    9. 2017-2018-1 20179205《Linux内核原理与设计》第八周作业

      <Linux内核原理与设计>第八周作业 视频学习及操作分析 预处理.编译.链接和目标文件的格式 可执行程序是怎么来的? 以C语言为例,经过编译器预处理.编译成汇编代码.汇编器编译成目标代码 ...

    随机推荐

    1. C跟C++

      工作以来几乎就只写C,下周开始的新工作可能就要开始写C++啦~ C: C++: 1.template 2.多态.重载.继承 3.虚函数 C++内存布局 看两张耗子的图片就清楚了.其中虚函数表一般放在数 ...

    2. 《Effective C#》快速笔记(四)- 使用框架

      .NET 是一个类库,你了解的越多,自己需要编写的代码就越少. 目录 三十.使用重写而不是事件处理函数 三十一.使用 IComparable<T> 和 IComparer<T> ...

    3. C# 知识回顾 - 匿名方法

      C# 基础回顾 - 匿名方法 目录 简介 匿名方法的参数使用范围 委托示例 简介 在 C# 2.0 之前的版本中,我们创建委托的唯一形式 -- 命名方法. 而 C# 2.0 -- 引进了匿名方法,在 ...

    4. 扩展SplitContainer控件

      效果图: 自定义控件实现代码: using System; using System.ComponentModel; using System.Drawing; using System.Window ...

    5. centos7编译安装redis遇坑

      编译redis时:make cc Command not found 原因分析:没有安装gcc,执行: yum install gcc 编译redis时:error: jemalloc/jemallo ...

    6. ASP.NET MVC4中使用bootstrip模态框时弹不出的问题

      最近发现使用在MVC中使用bootstrip的模态框时弹不出来,但单独建立一HTML文件时可以弹出,说明代码没有问题,经过多次测试发现,在MVC的cshtml文件中添加上以下语句就能正常 @{ Lay ...

    7. usebean 使用语法

    8. POJ1741:Tree——题解+树分治简要讲解

      http://poj.org/problem?id=1741 题目大意:给一棵树,求点对间距离<=k的个数. ———————————————————— 以这道题为例记录一下对于树分治的理解. 树 ...

    9. poj2409:Let it Bead(置换群 polya定理)

      题目大意:长度为n的项链,要染m种颜色,可以通过旋转或翻转到达的状态视为同一种,问有多少种染色方案. 学了一波polya定理,发现很好理解啊,其实就是burnside定理的扩展. burnside定理 ...

    10. 背景建模技术(五):视频捕获(VideoCapture)模块

      本次对“视频捕获(VideoCapture)模块”做出分析,给出源代码和对应的程序流程框架. 视频捕获模块的主要功能是设置视频或相机参数,并读取设置配置参数,最后进入帧处理模块的process进程,该 ...