第四章 抽象:进程
将程序的运行抽象为一个进程,使多个进程可以并发执行。
虚拟化CPU提供假象:有多个CPU
现在的CPU都是多核多线程的,也就是说,可以线程之间可以并行执行。
注:线程和进程的区别:起初是单CPU时代,程序的每次运行都会进行资源的分配,这时候,进程就是分配资源的最小单位;同时,单CPU时代,也没有引入线程的概念,所以,那时候,进程也是调度的最小单位。当进入多CPU多线程的时候,引入了线程:线程是最小的调度单位。所以,现在的进程是一个资源分配的最小单位,线程是调度的最小单位,一个进程的资源可以被多个线程共享 接下来的讨论,都是在单CPU的情况下,也就是没有线程的概念,进程也是参与调度的单位
即使是现在有了多线程,可以让多个进程的线程并行,但是,可以看到,即使每个进程只有一个线程,运行着的线程也远远的多于CPU提供的线程数。
因此,我们进行虚拟化,提供有更多CPU(线程)的假象。办法就是通过时间片的轮转,每个进程(线程)运行一段时间,然后切换到下一个进程(线程)。
进程状态
首先来讨论理论上必需的三种状态:
- 运行:进程正在占用CPU
- 就绪:进程具备运行的所有资源,等待被调度
- 阻塞:进程需要请求资源,如I/O请求。
三态转换
操作系统实现了多种状态
除了必需的三种状态,除了这些,进程也会处于初始状态(进程没有完全创建完成);
进程已退出但没有清理的“僵尸”状态;
有的操作系统还会有挂起操作,将进程的数据等运行时资源转入外存,让出内存空间,这时又会有新的状态。
数据结构
操作系统需要一些关键的数据结构来跟踪进程的星官信息。
xv6的进程信息类型:原文链接https://github.com/mit-pdos/xv6-public
struct context {
uint edi;
uint esi;
uint ebx;
uint ebp;
uint eip;
};
enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
// Per-process state
struct proc {
uint sz; // Size of process memory (bytes)
pde_t* pgdir; // Page table
char *kstack; // Bottom of kernel stack for this process
enum procstate state; // Process state
int pid; // Process ID
struct proc *parent; // Parent process
struct trapframe *tf; // Trap frame for current syscall
struct context *context; // swtch() here to run 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
char name[16]; // Process name (debugging)
};
数据结构:进程列表
为了追踪系统中运行的所有进程,将这些信息组织成进程列表;进程列表中的每个个体,保存一个进程的信息。这个个体结构也被称作进程控制块(Process Control Block, PCB)