课程学习总结报告

前言:

这门课程主要分为这两部分:

* 前半部分(孟宁老师)    初级:基础、系统调用、进程及实验

* 后半部分(李春杰老师) 高级:中断、内存、文件…

结合这次作业的机会,对所学知识构造概念模型,系统的认识Linux系统

一:Linux基础结构模型

课程学习总结报告_第1张图片 

由基本模型,可以分为用户态和内核态。

  • 用户态:即上层应用程序的活动空间,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。在⽤户态,代码能够掌控的范围会受到限制。
  • 内核态:所有的指令包括特权指令都可以执⾏。
  • ⽤户态和内核态很显著的区分⽅法就是CS:EIP 的指向范围:

 

            在内核态时,CS:EIP 的值可以是任意的地址,在 32 位 Linux 系统上有 4GB 的进程地址空间,内核态下的这 4GB 的地址空间全都可以访问。

 

 

            在⽤户态时,只能访问 0x00000000~0xbfffffff 的地址空间,0xc0000000 以上的地址空间只能在内核态下访问
课程学习总结报告_第2张图片

 

 

 

在了解内核之前,我们可以对计算机系统的基本⼯作原理做一个简单的梳理:

  • 冯·诺依曼体系结构的主要特点:CPU和内存是计算机的两个最主要组成部分。内存中保存着数据和程序指令,CPU从内存中取指令执⾏,其中有些指令让CPU做运算,有些指令让CPU读写内存中的数据。 课程学习总结报告_第3张图片
  • 冯·诺依曼体系结构的核⼼是存储程序计算机。

  • 存储程序计算机的主要思想是将程序存放在计算机存储器中,然后按存储器中的存储程序的⾸地址。执⾏程序的第⼀条指令,以后就按照该程序中编写好的指令执⾏,直⾄程序执⾏结束课程学习总结报告_第4张图片

     
  • 可以把CPU抽象成⼀个for循环,因为它总是在执⾏next instruction(下⼀条指令),然后从内存⾥取下⼀条指令来执⾏。
  • 从这个⻆度来看,内存保存指令和数据,CPU负责解释和执⾏这些指令,它们通过总线连接起来。
  • 从内存读⼀个数到寄存器的执行过程: 
  • 1)CPU通过控制总线RD发⼀个读请求,并且将内存地址通过地址总线A0-A31发给内存;
  • 2)内存芯⽚收到地址和读请求之后,将相应的内存单元对接到数据总线D0-D31;这样,内存单元每⼀位的1或0状态通过⼀条数据线到达CPU寄存器中相应的位,就完成了数据传送。

     课程学习总结报告_第5张图片

     

     

     

Linux内核实现了操作系统的三⼤核⼼功能,即进程管理、内存管理和⽂件系统。是连接计算机软硬件的桥梁,起到了非常关键的承上启下作用。它提供了各式各样基础且关键的API接口,供上层库函数和程序通过系统调用来使用;同时,它可以直接对接硬件设施,实现基本功能。

按照功能模块,再细分成如下图所示模型:

  • 进程管理:保证各个进程能够合理地利用CPU资源完成自身任务。
  • 内存管理:管理内存资源,使得内存被进程安全使用。         

  • 虚拟文件系统:将硬盘、光盘等外设抽象为虚拟文件,Linux内核提供诸如open、write、read等接口函数对其统一管理。

  • 网络系统:管理涉及网络操作的资源。

  • 进程间通信:为进程间交流资源提供渠道。 

 

二:Linux内核的启动过程

课程学习总结报告_第6张图片

  • 存放Linux内核启动时的初始化代码放在init⽬录中。init⽬录中的main.c源⽂件负责整个Linux内核启动,⽽main.c源⽂件中的start_kernel函数是Linux内核启动过程的起点。此函 数会初始化高层内核功能,安装根文件系统,以及启动 init 进程
  • Init 进程:启动各种应用程序(根据相关启动脚本设置)。Init 进程首先进行一系列的硬件初始化,并挂载根文件系统。最后 init 进程会执行用户传递过来的“init=”启动参数执行用户指定的命令,或者执行以下几个进程之一,由内核态变为用户态: execve("/sbin/init",argv_init,envp_init);

三:进程管理

进程:是进程实体 的运行过程,是系统进行资源分配和调度的一 个独立单位

  • 进程实体由进程控制块(PCB),程序段,相关的数据段组成,动态性,并发性,独立性,异步性
  • 进程控制块:进程控制块PCB是名字为task_struct的数据结构,它 称为任务结构体。当一个进程被创建时,系统就为该进程建立一个 task_struct任务结构体。当进程运行结束时,系统撤 消该进程的任务结构体。进程的任务结构体是进程存在的唯一标志。
  • 进程上下文:把系统提供给进程的处于动态变化的运行环境总和称为进程上下文。
  • 进程栈:linux系统为每个用户进程分配了两个栈:用户栈和内核栈。内核进程只有内核栈,没有用户栈。
  • 进程链表:为了对给定类型的进程进行有效的搜索,内核维护了几个进程链表。一般进程链表是双向链表。
  • 进程的状态:运行态、可运行态、等待态、暂停态、僵死态。
  • 课程学习总结报告_第7张图片

     

     

进程切换本质上由两步完成:

  1. 切换页全局目录以安装一个新的地址空间
  2. 切换内核态堆栈和硬件上下文,例如:CPU寄存器等

调度时机

  1. 进程状态发生变化
  2. 当前进程时间片用完
  3. 进程从系统调用返回到用户态
  4. 中断处理后,进程返回到用户态

Linux进程调度策略

  • linux的进程调度是基于优先级的调度。进程的优先级是动态的,避免了进程饥饿。
  • linux进程分为普通进程和实时进程,实时进程的优先级高于普通进程。实时进程的优先级范围为1~99
  • 普通进程采用普通进程的时间片轮转算法
  • 实时进程采用实时进程的先进先出或实时进程的时间片轮转算法

四:中断机制

从⽤户态进⼊内核态是由中断触发的,那么中断发送的时机:

  • 可能是硬件中断,在⽤户态进程执⾏时,硬件中断信号到来,进⼊内核态,就会执⾏这个中断对应的中断服务例程。
  • 也可能是⽤户态程序执⾏过程中,调⽤了⼀个系统调⽤,陷⼊了内核态,叫作陷阱(trap)(系统调⽤是特殊的中断)。

 

中断上下文切换:

  • CPU上下⽂的切换,⽐如中断信号/中断指令、iret中断返回指令
  • 保存现场和恢复现场,中断处理程序的头部和尾部

 

中断可分为

1)外部中断(硬件中断

2)内部中断(软件中断)/异常:

处理器探测异常:由CPU执行指令时探测到一个反常条件时产生,如溢出、除0错等。

对于处理器探测异常,根据异常时保存在内核堆栈中的eip的值可以进一步分为

  • 故障(fault):eip=引起故障的指令的地址,通常可以纠正,比如缺页异常
  • 陷阱(trap):eip=随后要执行的指令的地址

编程异常:由编程者发出的特定请求产生,通常由int类指令触发,比如系统调用

 

根据中断向量找到异常处理程序:

  • 首先,任何中断 事件发生,CPU都会得到一个对应的中断向量
  • 中断向量=>中断描述符表的对应项(中断描述符:在内核初始化时申请一段内存即IDT表的位置,由idtr寄存器保存这段内存的基地址,CPU通过(idtr)+n*8获取描述符地址(因为一个中断描述符是8个字节))=>获取中断处理程序的eip+cs(即逻辑地址)=>转去中断处理程序

cpu的执行:

  • 执行指令, cs,eip记录了下一条要执行的指令的逻辑地址。
  • 执行指令之前,CPU控制单元会检查前一条指令运行时是否发生了中断或异常
  • 如果发生了中断或异常:
  •    1. 确定中断或异常的向量i
  •    2. 从idtr寄存器中找到IDT的地址,从IDT中找到第i项
  •    3. 从gdtr寄存器中找到GDT的地址,从GDT找到IDT项中的段选择符所标识的段描述符
  •    4. 确定中断是由授权的发生源发出
  • 检查是否发生了特权级变化,一般指是否由用户态变为内核态
  •     1. ss、esp、eflags、cs、eip依次入栈
  •     2.硬件出错码入栈
  • 如果发生的是故障,在异常处理后再次执行

中断/异常处理完后,相应的处理程序会执行一条iret汇编指令,这条汇编指令让CPU控制单元做如下事情:

  1. 用保存在栈中的值装载cs、eip和eflags寄存器。如果一个硬件出错码曾被压入栈中,那么弹出这个硬件出错码
  2. 检查处理程序的特权级是否等于cs中最低两位的值(这意味着进程在被中断的时候是运行在内核态还是用户态)。若是表明依然在内核态,处理中断嵌套,iret终止执行;否则,中断返回转入3
  3. 从栈中装载ss和esp寄存器。这步意味着返回到与旧特权级相关的栈
  4. 检查ds、es、fs和gs段寄存器的内容,防止恶意用户程序利用这些寄存器访问内核空间

中断程序入口操作

  • 将中断向量入栈
  • 保存所有其他寄存器 SAVE_ALL,按照pt_regs数据结构保存现场
  • 调用do_IRQ,见下图
  • 跳转到ret_from_intr

do_IRQ使用的数据结构

课程学习总结报告_第8张图片

课程学习总结报告_第9张图片

 

 

 

五:文件系统

文件打开:

  • 应用程序对open ( )的调用将引起内核调用服务例程sys_open ( )函数,该函数接收的参数为:要打开文件的路径名和访问模式等;
  • 该系统调用成功后将返回一个文件描述符,也就是文件对象指针数组的一个索引;
  • 系统调用不成功时返回-1。

文件关闭:

  • 用户程序通过close ( )系统调用关闭打开的文件,该函数接收的参数为要关闭文件的文件描述符。
  • 内核服务例程为sys_close ( )函数。

读文件大概流程如下:

  • 进程调用库函数向内核发起读文件请求;
  • 内核通过检查进程的文件描述符定位到虚拟文件系统的已打开文件列表表项;
  • 调用该文件可用的系统调用函数read()。read()函数通过文件表项链接到目录项模块,根据传入的文件路径,在目录项模块中检索,找到该文件的inode;
  • 在inode中,通过文件内容偏移量计算出要读取的页;
  • 通过inode找到文件对应的address_space;
  • 在address_space中访问该文件的页缓存树,查找对应的页缓存结点:
  •          如果页缓存命中,那么直接返回文件内容;
  •          如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页;重新进行第6步查找页缓存;
  • 文件内容读取成功。

虚拟文件系统(Virtual File System, 简称 VFS):

  • 是 Linux 内核中的一个软件层,用于给用户空间的程序提供文件系统接口;
  • 同时,它也提供了内核中的一个抽象功能,允许不同的文件系统共存。
  • 系统中所有的文件系统不但依赖 VFS 共存,而且也依靠 VFS 协同工作。
  • 为了能够支持各种实际文件系统,VFS 定义了所有文件系统都支持的基本的、概念上的接口和数据 结构;
  • 同时实际文件系统也提供 VFS 所期望的抽象接口和数据结构,将自身的诸如文件、目录等概念在形式 上与VFS的定义保持一致。一个实际的文件系统想要被 Linux 支持,就必须提供一个符合VFS标准 的接口,才能与 VFS 协同工作。实际文件系统在统一的接口和数据结构下隐藏了具体的实现细节,所以在VFS 层和内核的其他部分看来,所有文件系统都是相同的。

六. 有关课程改进的建议就是:

对过程的演示可以多一点,就像孟老师讲堆栈操作演示的那样,使得讲解更加的生动深刻,易于学习。

最后,感谢老师们在疫情期间依然一丝不苟地为我们细致地讲课。

 

 

 

你可能感兴趣的:(课程学习总结报告)