课程学习总结报告

  本次博客作为课程总结,总述了这门课上了解到的一些linux系统的知识,大致分为计算机系统的基本工作原理、进程管理、中断与系统调用、文件系统这几部分,基本上是一些课堂要点的复现。最后还有本门课学习的心得体会以及对课程的建议。

一、计算机系统的基本工作原理

1.1.存储程序计算机和冯诺依曼结构

  存储程序计算机的主要思想是将程序存放在计算机存储器中,然后按存储器中的存储程序的⾸地址执⾏程序的第⼀条指令,以后就按照该程序中编写好的指令执⾏,直⾄程序执⾏结束。它为现在的各种硬件、终端奠定了基调。冯诺依曼结构由运算器、存储器、控制器、输⼊设备和输出设备5⼤基本类型部件组成。它是现代计算机的原型。

1.2.函数调用堆栈 

  堆栈是C语⾔程序运⾏时必须的⼀个记录调⽤路径和参数的空间。cpu中的两个寄存器ebp和esp分别指向堆栈的栈底和栈顶。

  a)call指令

     cs : eip原来的值指向call下⼀条指令,该值被保存到栈顶,然后cs : eip的值指向xxx的⼊⼜地址

  b)进入被调函数后,通过:

     pushl %ebp

     pushl %esp, %ebp

    建立被调用函数的堆栈框架

  c)被调函数运行完毕后,通过:

     movl %ebp, %esp

     popl %ebp

     ret

     拆除被调用函数的堆栈框架

  d)ret指令

     从栈顶弹出原来保存在这⾥的cs : eip的值,放⼊cs :eip中

二、进程

2.1.进程的描述和状态

进程的描述:

  通过进程控制块——PCB来刻画进程,感知进程的存在。在Linux内核中,⽤⼀个结构体task_struct来描述进程:

  

  其中,thread的类型是一个thread_struct结构体,用于保存进程上下⽂切换过程中CPU相关的⼀些状态信息的关键数据结构,比如保存寄存器ip,esp的旧值。

linux中进程的状态:

  linux中进程的状态是由上面的结构体task_struct中的成员state来描述的,它可以取的值比较多,如TASK_RUNNING(就绪或者正在运行),TASK_INTERRUPTIBLE(可中断睡眠态),TASK_UNINTERRUPTIBLE(不可中断睡眠态)等等,如下图:

2.2. 进程的切换

  我们在mykernel中实验中实现了简易的进程切换。核心的步骤为:

  1.硬编码构建第一个进程的运行堆栈,并让其运行

  2.通过thread中的ip、sp,完成esp、ebp以及eip寄存器的重定向,指向即将运行的进程

  具体切换过程:

  

  1. 保存push rbp,将prev进程的rbp的值压栈
  2. 把rsp的值保存到它自己的sp变量中
  3. 之后将next进程的sp变量中的值赋给rsp寄存器(完成栈帧的转换)
  4. 并保存prev进程的rip
  5. 随后压栈next进程的ip,也即$1(这里已经是在next的栈帧中操作了)
  6. 这之后return, 也就是pop next进程的rip,执行next进程也就是$1后面的语句
  7. pop rbp,将rbp指向next进程的栈底

 2.3.进程的维护与调度

  2.3.1进程的维护  

  首先,0号进程是所有进程的祖先,它首先创建了1号和2号进程,1号进程是所有用户态进程的祖先,2号进程又创建了所有内核线程。使用PID (Process IDPID)进程标识符的数来标识进程。为了对给定类型的进程(比如所有在可运行状态下的进程)进行有效的搜索, 内核维护了几个进程链表。一个是所有进程链表,一个是可运行进程的链表,也叫运行队列。如下图:

  其中又分为活动队列active和过期队列expire,活动队列为运行过的进程组成的队列,expire相反。每个链表都通过prio_array_t 结构来实现,它的具体结构为:

  

   2.3.2进程的调度

   linux的进程调度基于优先级。具体的,进程分为普通进程和实时进程。在基于优先级的算法下实时进程的优先级高于普通进程。其次,Linux中进程的优先级是动态的, 调度程序周期性的调整他们的优先级, 避免进程饥饿。有如下三种策略:

三、中断与系统调用

  广义上的中断(或称中断信号)可分为外中断和内中断,外中断是异步的,内中断时同步的,也即异常。而系统调用就是一种异常。

  中断或异常的执行过程:

  1. 确定与中断或异常关联的向量i(0 <= i <= 255)。

  2. 读中断描述符表(IDT)中的第i项。

  3. 通过第i项中的段选择符在GDT中查找相应的段描述符。后者指定中断或异常处理程序的段基地址。

  4. 权限检查:比较CPL与段描述符的DPL;比较CPL与门描述符的DPL。

  5. 检查是否发生了特权级的变化,若CPL不同于段描述符的DPL(从用户态陷入内核态),则:

    1. 读取tr寄存器访问运行进程的TSS段。

    2. 装载TSS中的ss与sp到寄存器(指向当前线程的内核栈)。

    3. 在新栈中保存ss和sp之前值。

  6. 若发生的是故障则用引起异常的指令修改cs和sp寄存器的值,以使这条指令在异常处理结束后可以被再次执行。

  7. 在栈中保存flags,cs和ip的值。

  8. 如果异常产生一个异常操作码,则将它保存在栈中。

  9. 装载处理程序入口地址到cs和ip。

  • 从中断或异常返回时要执行iret指令,CPU会执行以下操作:

  1. cs、ip、flags依次出栈并装载到相应寄存器。若有硬件出错码,则将其出栈。

  2. 权限检查:比较CPL与处理程序权限等级,若相等,则结束;否则(从内核态返回用户态),执行下一步。

  3. 从栈中装载ss和sp寄存器(指向被中断线程的用户栈)。

  4. 检查并重置ds,es,fs,gs寄存器。

四、文件系统

4.1 三个重要的数据结构

  三个重要的数据结构:file_operation——包含了文件操作,File——系统文件打开表,inode:索引节点,即FCB。以及另一个数据结构file_struct,用户文件打开表,

用来记录文件描述符的使用情况,该结构中有fd数组,该数组中的某一项即指向一个File(open系统调用后产生)

4.2 VFS

  由于linux支持各种不同的文件系统,故要为用户程序提供一个统一的、抽象的、虚拟的、屏蔽不同文件系统细节的接口,因此需要VFS来支持。

 

   VFS主要的数据结构有:1.超级块对象 2.索引节点对象 3.目录项对象 4.文件对象

五、心得体会

  本门课程孟老师的部分注重实践,几个深入浅出的实验带我们了解了进程的切换、嵌入式汇编、内核的编译、根文件系统的挂载、系统调用等等内容,收获还是不少的。李老师的部分更偏向理论讲解,内容扎实,印象深刻的内容就是中断的保存现场和找到中断处理程序的整个流程,讲的比较细致。

  对这们课的建议就是,孟老师的ppt上成段的文字较多,最好能添加一些概括性的小标题,增强一下内容之间的逻辑联系。

  

原文地址:https://www.cnblogs.com/hhssqq9999/p/13268645.html