进程由哪些部分组成?进程上下文又由哪些部分组成?

进程的组成

  1. 进程控制块(Program control block)(灵魂)

    • 建立进程——建立PCB
    • 撤销PCB——销毁进程
  2. 程序(躯体)

    • 代码(code)
    • 数据(data)
    • 堆和栈(stack和heap)(栈:保存返回点、参数、返回值、局部变量堆:动态变量)

进程的地址空间

内核空间1G,用户空间3G

一般来说进程的地址空间指的是用户空间

alt text

进程详细的组成部分

alt text

进程的上下文:

  • 物理实体(代码和数据,在地址空间)
  • 支持进程运行的环境(PCB、内核栈、reg)

具体来说:

  • 进程的程序块、数据块、运行时的堆和用户栈等组成的用户空间信息是用户级上下文,也就是地址空间
  • 寄存器(reg)是硬件上下文(寄存器上下文),即进程的现场信息
  • PCB、内核栈等内核信息是系统级上下文

进程上下文切换:

  • 总的来说,下降进程的上下文保存,上升进程中曾经被保存的上下文重新放到被执行的环境中。
  • 在进程上下文切换过程中,OS把换下进程的寄存器上下文保存到系统级上下文(理论上在PCB,实际上PCB很小因此保存在内核栈中,通过stack指针找到)
  • 用户级上下文和系统级上下文一起构成了一个进程的整个存储器映像

何时发生进程上下文切换?

下降进程自身造成切换/外界强制下降进程和上升进程切换

注意,要和CPU上下文区分开,CPU的上下文切换分为几种场景:进程上下文切换、线程上下文切换、中断上下文切换

系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,也不会切换进程。 系统调用过程通常称为特权模式切换,而不是进程上下文切换,进行的是线程上下文切换

下降进程的现场和断点保存在哪里?是PCB吗?

现场就是寄存器,断点就是PC寄存器。前面已说,理论上在PCB,实际上在内核栈

用户栈和内核栈(中断栈)

每个进程(线程)两个栈:

alt text

tip:因为上下文切换是由中断驱动的,所以内核栈也叫中断栈。不过IA32将内核栈和中断栈分开,ARM体系结构两者完全一样

CPU硬件的中断响应

基本流程

  1. 关中断(“中断允许位”自动清0):使CPU处于禁止中断状态,以防止新中断破坏SP、断点(PC)、程序状态字(PSW)等

  2. 保存断点和中断状态:将用户栈指针、断点、程序状态字(PSW)保存到内核栈

    • SP(SS:ESP)
    • PC(CS:EIP)
    • PSW,在IA-32中是EFLAGS寄存器

    为什么要先保存SP?

    总的来说,SP只有一个且SP首先发生变化

    因为下降进程,它的硬件上下文要保存到PCB或是内核栈中,既然要保存到内核栈,SP就必须由本来用户态时指向用户栈,而改为指向内核栈,这说明SP首先发生变化,故而在将硬件上下文保存到内核栈时,首先要保存SP。之所以知道内核栈的位置,以linux的PCB,即task_struct为例,PCB中有指向本进程内核栈的指针,可以通过PCB找到内核栈

  3. 识别中断事件,得到“中断类型号”,根据此号,到中断向量表中读取对应的中断服务程序的入口地址

中断向量表

alt text

具体流程:

中断发生前:

alt text

中断发生时:

alt text

总结

shell进程和hello进程的例子

进程切换必须由操作系统接管控制权

shell里面运行一个hello程序

alt text

进程切换还需要改变当前进程空间

模式切换:CPU还是在同一进程中运行或在在中断上下文时,因此还需要改变进程空间

  1. 保存当前进程的硬件上下文,对Linux系统而言,硬件上下文大部分(SP/PC/PSW等)保存在struct thread_struct thread中,通用寄存器(eax/ebx)保存在内核栈
  2. 修改当前进程的PCB, 比如将其运行状态由运行态改为就绪或者等待,并将该PCB加入相关队列
  3. 调度另外一个进程
  4. 修改被调度进程的PCB, 状态改为运行(系统上下文)
  5. 将“当前进程”的存储管理数据改为被调度进程的存储管理(页表、TLB)(用户级上下文)
  6. 恢复新进程的硬件上下文,让PC指向新进程代码