• 课程学习总结报告


    一、linux系统概念模型

    1. 概述

    linux系统是一个多用户多任务的分时操作系统,函数调用是操作系统的三大法宝之一,使得编程极为灵活。由于CPU的运行速度远远大于外设,所以中断机制的使用解决了CPU等待外设的情况。系统调用是一种特殊的中断,封装了对系统的一些底层的操作,保证了系统的安全性。在中断返回时可能发生进程切换,内核线程也可以主动发起进程切换

    本篇博文是对整个Linux操作系统分析课程的整体回顾,下面对上述焦点问题分别详细阐述

    2. 函数调用

    函数调用过程概述

    • 首先调用者的call函数将它的下一条指令地址保存在栈顶,将eip(rip)设置为被调用函数的起始地址

    • 建立被调用者函数的堆栈框架、执行被调用者函数体、拆除被调用者函数框架

    • ret指令将调用者的下一条指令地址恢复到eip(rip)

    函数调用中的硬件操作

    例如对于函数调用:call 0x12345

    /*32位*/
    pushl %eip (*) 
    movl $0x12345, %eip (*) 
    
    /*64位*/
    pushq %rip (*)
    movq $0x12345, %rip (*) 
    

    函数返回:ret

     /*32位*/
     popl %eip (*)
     
     /*64位*/
     popq %rip (*)
    

    上面的指令均为伪指令,这个动作由硬件一次性完成,这是由于EIP寄存器不能被直接修改和使用

    函数调用中的软件操作

    例如对于函数:

    int main() {
    	return f(8)+1;
    }
    

    在32位系统中,汇编为:

    main:  
    	pushl %ebp
    	movl %esp, %ebp
    	subl $4, %esp
    	movl $8, (%esp)
    	call f
    	addl $1, %eax
    	movl  %ebp,%esp
    	popl  %ebp
    	ret
    

    在64位系统中,汇编为:

    main:
    	pushq %rbp
    	movq %rsp, %rbp
    	movl $8, %edi
    	call f
    	addl $1, %eax
    	popq %rbp
    	ret
    

    我们对比32位系统和64位系统的函数调用:

    1. 共同点:

      • 都在调用之前保存了原来的栈,为新函数开辟了新的栈空间

      • 调用返回时都恢复了原来的栈空间

      • 函数的返回值都在eax(rax)寄存器中保存

    2. 区别:

      • 参数传递上存在差别,32位系统将参数存在新栈的栈底

    3. 中断和异常

    中断和异常的区别与联系:

    中断是异步的,由硬件随机产生,在程序执行的任何时候可能出现

    异常是同步的,在特殊的或出错的指令执行时由CPU控制单元 产生

    我们用“中断信号”来通称这两种类型的中断

    中断上下文

    中断上下文不同于进程上下文,中断上下文只包含了很有限的几个寄存器,建立和终止这个上下文所需要的时间很少。中断程序只能使用被中断进程的内核栈作为自己的运行栈

    中断和异常的硬件级处理

    • 当CPU正常运行时,执行一条指令后,cs和eip包含了下一条将要执行的指令的逻辑地址
    • 在执行这条指令之前,CPU会检查在运行前一条指令时是否发生了一个中断或者异常。
    • 如果发生了中断或异常:
      • 确定中断向量号i,读idtr寄存器指向的IDT表中的第i项
      • 从gdtr寄存器获得GDT的基地址,并在GDT中查找, 以读取IDT表项中的段选择符所标识的段描述符
      • 确定中断是由授权的发生源发出的,只允许从低特权级陷入高特权级,反之不可以
      • 如果是由用户态陷入内核态,用与新特权级相关的栈段和栈指针装载ss和esp寄存器,在新的栈中保存ss和esp以前的值
      • 如果发生的是故障,用引起异常的指令地址修改cs 和eip寄存器的值,以使得这条指令在异常处理结束后能被再次执行
      • 在栈中保存eflags、cs、eip;如果异常产生一个硬件出错码,则将它保存在栈中
      • 装载cs和eip寄存器,其值分别是IDT表中第i项描述符的段选择符和偏移量
    • 中断返回:
      • 用保存在栈中的值装载cs、eip和eflags寄存器
      • 检查中断时是在内核态还是用户态,如果在内核态,则终止;如果在用户态,从栈中装载ss和esp寄存器

    中断和异常的软件级处理

    异常处理:

    • 按照pt_regs结构定义的堆栈数据格式完成相应的入栈操作,进一步完成现场的保存

    • 把堆栈地址中的do_handler_name()函数的地址装入edi寄存器中,并在这个位置写入fs值,使栈结构进一步与pt_regs结构完全一致

    • 最后执行call *%edi指令

    中断处理:

    可以用下面的汇编代码来总结:

    SAVE_ALL
    movl %esp,%eax
    call do_IRQ jmp
    $ret_from_intr
    

    4. 系统调用

    系统调用是陷阱这种软中断方式主动从用户态进入内核态的

    传统的系统调用:

    int $0x80 指令会触发系统调用,CPU压栈⼀些关键寄存器,根据eax寄存器传递的系统调用号调用对应的内核处理函数,接着内核负责保存现场,系统调用内核函数处理完后恢复现场,后通过iret出栈哪些CPU压栈的关键寄存器。

    快速系统调用:

    sysenter和syscall都借助CPU内部的MSR寄存器来查找系统调用处理入口,其余操作与传统系统调用相同

    系统调用的参数传递:

    32位x86和64位的x86都是通过寄存器来传递。注意:在普通的函数调用中,32位x86使用压栈来传递参数,而64位x86仍然使用寄存器传递参数

    5. 进程管理

    进程创建

    0号进程初始化是通过硬件编码,其他进程的初始化都是通过do_fork复制父进程的方式初始化

    1号进程为是kernel_init,是所有用户进程的祖先;2号进程时kthreadd,是所有内核线程的祖先,1、2号进程都是复制0号进程得到的

    父进程通过fork系统调用进入内核_do_fork函数,主要完成了:

    • 调用copy_process()复制父进程
    • 分配子进程的内核堆栈并对内核堆栈和thread等进程关键山下文进行初始化
    • 调用wake_up_new_task将子进程加入就绪队列等待调度执行

    进程切换

    进程调度时机: Linux内核通过schedule函数实现进程调度,分别为中断返回前和内核线程主动调用schedule

    进程上下文:

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

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

    • 进程的CPU上下文,相关寄存器的值

    当调用schedule函数时其中的switch_to做了关键的进程上下文切换。将当前进程X的内核堆栈切换到进程调度算法选出来的next进程的内核堆栈,并完成了进程上下文所需的EIP等寄存器状态切换

    6. 文件系统

    文件对用户来说是最直接可见的部分

    linux中文件时按名存取的,系统给每个文件设置一个文件控制块——FCB

    文件系统的一个重要的任务是文件系统的存储空间的管理,实质上是空闲块的组织和管理分配和回收等问题

    用户可以使用系统调用来实现文件操作,如open、read、write、close

    虚拟文件系统:要实现操作系统对不同文件系统的支持,就要将对各种不同文件系统的操作和管理纳入到一个统一的框架中。对用户程序隐藏实现细节

    二、Linux模型的举例

    我们举一个在linux命令行输入vim,并按下回车的例子:

    我们在键盘输入回车键系统能有反应,说明发生了中断,该中断处理程序处理了键盘的输入信号

    中断返回的时候,父进程fork系统调用进入内核_do_fork函数

    调用copy_process()复制父进程

    分配子进程的内核堆栈并对内核堆栈和thread等进程关键山下文进行初始化

    调用wake_up_new_task将子进程加入就绪队列等待调度执行

    Linux内核通过schedule函数实现进程调度

    schedule函数中的switch_to做了关键的进程上下文切换,将之前进程的内核堆栈以及相关寄存器切换到vim进程上来,这样我们就看到进入了vim程序的界面

    三、对课程的心得体会

    本课程两位老师通过理论和实验双管齐下,对linux系统的原理进行了深刻的阐述,使得本人对Linux操作系统自有的特性有了一定的认识,不再像之前只是操作系统这个笼统的概念。

    改进意见:增加一些个在实际工程中使用Linux的实验,这样可以让同学们在应用中从不同的角度理解Linux系统

  • 相关阅读:
    Swoole实战之手撸HttpServer框架 16 关于服务的平滑重启与热加载与cli_set_process_title函数的使用、添加前置进程、修改代码服务器自动热更新
    Swoole实战之手撸HttpServer框架 17 里程碑 整合前置进程、热更新、Ioc容器
    axios取消请求
    echarts使用transform缩放后导致图标模糊
    vuex4的简单使用
    Vite 按需引入 Ant Design Vue 3.0
    TS声明promise返回来的数据类型
    JavaScript一种新的数据结构类型Map
    CF1453D Checkpoints(期望)
    PLOP: Learning without Forgetting for Continual Semantic Segmentation论文阅读笔记
  • 原文地址:https://www.cnblogs.com/happyyouli/p/13275470.html
Copyright © 2020-2023  润新知