进程综述
定义
进程是正在执行程序的实例,包括程序计数器、寄存器和变量的当前值。
进程不只是程序代码,程序代码称为文本段(代码段),还包括当前活动,通过程序计数器的值和处理器寄存器的内容来表示。此外,进程还包括进程堆栈段(临时数据、函数参数、局部变量、地址)和数据段(包括全全局变量。还可能包括堆(heap),是在进程运行期间动态分配内存。
程序和进程的关系:
- 程序是被动实体,进程是活动实体(进程有开始,有结束、程序没有;进程会走走停停,程序走停无意义)
- 一个程序可以对应多个进程(不同进程间代码段相同,而数据段、堆栈等不同)
为什么要有进程这个概念
核心目的:虚拟化CPU,从而支持多个进程在少量的物理CPU上运行(每个进程以为它独享一个CPU)。
操作系统通过虚拟化(virtualizing)CPU 来提供这种假象。通过让一个进程只运行一个时间片,然后切换到其他进程,操作系统提供了存在多个虚拟 CPU 的假象。这就是时分共享(time sharing)CPU 技术,允许用户如愿运行多个并发进程。潜在的开销就是性能损失, 因为如果 CPU 必须共享,每个进程的运行就会慢一点(上下文切换)。
进程创建
核心问题:程序是如何转化为进程的?(即:操作系统如何启动并运 行一个程序?进程创建实际如何进行?)
- 将代码和所有静态数据(例如初始化变量)加载(load)到内存中,加载到进程的地址空间中。
- 为程序的运行时栈(run-time stack 或 stack)分配一些内存。
- 为程序的堆(heap)分配一些内存。
- 执行一些其他初始化任务,特别是与输入/输出(I/O)相关的任务。(在 UNIX 系统中,默认情况下每个进程都有标准输入stdin,标准输出stdout,标准错误stderr三个文件描述符)
- 启动程序, 在入口处运行,即 main()。
进程状态
在三状态模型中,进程的三个状态如下:
- 运行(running):在运行状态下,进程正在处理器上运行。这意味着它正在执行指令。
- 就绪(ready):在就绪状态下,进程已准备好运行,但由于某种原因,操作系统选择不在此时运行。
- 阻塞(blocked):在阻塞状态下,一个进程执行了某种操作,直到发生其他事件时才会准备运行。
若加上新建态、退出态,则是五状态模型,两者本质上是一样的,三状态模型更能体现进程核心的状态。
三状态之间的转换如下图:
对应到操作系统中位以下图,处理器中运行的为运行态进程、就绪队列中为就绪态进程、阻塞队列中为阻塞态进程。
需要注意的是,运行态进程有三种转换去向:
- 发生I/O事件,进入阻塞队列,若I/O事件完成该进程则可转入就绪队列。
- 时间片到期,操作系统则抢占之,使之进入就绪队列,等待下一次调度。
- 进程完成,释放。
若考虑到I/O速度较慢且内存数量有限,只能容纳一部分的进程,因此存在内存中所有的进程都在等待I/O的情况。在这种情况中,CPU多数时间处于空闲状态。因此,将所有进程放入内存中是不理智的。可以通过将一部分进程换出到磁盘中,增大操作系统中的进程数目,从而充分利用CPU。
挂起(suspend),即将内存中某个进程的一部分或全部移到磁盘。当内存中不存在就绪态进程时,操作系统就把被阻塞的进程换出到磁盘中的挂起队列(suspend queue),即临时从内存中“踢出”的进程队列。
包含单挂的模型如下,相比与三/五状态模型,增加了一个挂起态:
单挂模型有个缺点:所有已被挂起进程都是阻塞态,那么将被阻塞的进程取回内存无任何意义。若I/O事件完成,阻塞态进程将不再阻塞,可以进入就绪态继续执行。因此可以将挂起态中的进程进行细分,即阻塞/挂起态及就绪/挂起态。
该模型中比较重要的状态转换为:
阻塞/挂起态->就绪/挂起态:若I/O事件完成,则将进程从阻塞/挂起态切换到就绪/挂起态。
进程描述
进程在操作系统内用进程控制块(process control block, PCB)来表示,PCB包含了进程状态、程序计数器、cpu寄存器、cpu调度信息、内存管理信息、记账信息、I/O状态信息等信息。
- 进程状态: 状态可包括新的,就绪,运行,等待,终止等。
- 程序计数器 : 计数器表示进程要执行的下个指令的地址。
- CPU寄存器: 与程序计数器一起,这些寄存器的状态信息在出现中断时也需要保存,以便进程以后能正确的执行。
- CPU调度信息:这类信息包括进程优先级、调度队列指针和其他调度参数。
- 内存管理信息:根据内存系统,这类信息包括基址和界限寄存器的值,页表或段表。
- 记账信息:这类信息包括CPU时间、实际使用时间、时间界限、记账数据、作业或进程数量等。
- I/O状态信息:这类信息包含分配给进程的I/O设备列表、打开的文件列表等。
进程之间的切换就用到PCB:
xv6中的进程描述信息如下:context即寄存器的上下文。
// the registers xv6 will save and restore
// to stop and subsequently restart a process
struct context {
int eip;
int esp;
int ebx;
int ecx;
int edx;
int esi;
int edi;
int ebp;
};
// the different states a process can be in
enum proc_state { UNUSED, EMBRYO, SLEEPING,
RUNNABLE, RUNNING, ZOMBIE };
// the information xv6 tracks about each process
// including its register context and state
struct proc {
char *mem; // Start of process memory
uint sz; // Size of process memory
char *kstack; // Bottom of kernel stack
// for this process
enum proc_state state; // Process state
int pid; // Process ID
struct proc *parent; // Parent process
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
struct context context; // Switch here to run process
struct trapframe *tf; // Trap frame for the
// current interrupt
};
reference
[1] 操作系统-精髓与设计原理
[2] 操作系统导论(ostep)
[3] 操作系统概念
[4] 现代操作系统