抽象:进程
进程就是运行中的程序。
一个正常的系统可能有上百个进程同时在运行。
关键问题:如何提供有许多CPU的假象? 虽然只有少量的物理CPU可用,但是操作系统如何提供几乎有无数个的CPU可用的假象?
时分共享CPU技术 通过允许资源由一个实体使用一小段时间,然后由另一个实体使用一小段时间如此下去。 与之对应的是空分共享,资源在空间上被划分给希望使用它的人。 例如:磁盘空间,一旦将块分配给文件,在用户删除文件之前,不可能将它分配给其他文件。
为了理解构成进程的是什么,我们必须理解他的机器状态:程序运行时可以读取和写入的数据也在内存中。因此进程可以访问的内存(成为地址空间)是该进程的一部分。
进程的机器状态的另一部分是寄存器。许多指令明确地读取或更新寄存器。
注意,有一些特殊的寄存器构成了该机器状态的一部分。例如:程序计数器(PC)(有时候称为指令指针IP)告诉我们程序当前正在执行哪条指令;类似的,栈指针和相关的栈指针用于管理函数参数栈、局部变量和返回地址。
进程API
- 创建 如:运行指定程序
- 销毁 操作系统能创建你就能销毁你,大部分程序还是自觉的,会自动退出
- 等待 有时候需要等待进程停止运行
- 其他控制 例如:暂定进程,停止一段时间然后继续运行。
- 状态 获得进程的状态信息,例如 运行了多长时间,处于什么状态。
进程创建的细节
程序如何转化为进程? 进程创建实际如何进行? 1. 将 代码和 所有静态数据(例如初始化变量)加载到内存中,加载到进程的地址空间中,程序最初以某种可执行格式驻留在磁盘上。因此,将程序和静态数据加载到内存中的过程,需要操作系统从磁盘读取这些字节,并将它们放在内存中的某处。
加载进程:.png
早起操作系统在运行程序前全部加载完成,现代操作系统惰性执行该过程,仅在程序执行期间需要的代码和数据片段才会加载。 要真正理解代码和数据的惰性加载是如何工作的,必须要了解分页和交换机制,这是未来要讨论的一个话题。
- 将代码和数据加载到内存后,操作系统在运行此进程之前还需要执行一些其他的操作,必须为程序的运行时栈分配一些内存。c程序使用栈存放局部变量、函数参数和返回地址。操作系统分配这些内存,并提供给进程。操作系统也有可能会用参数初始化栈。具体来说,它会将参数填入mian函数,即argc和argv数组。
- 操作系统也可能为程序的堆分配一些内存。在c程序中,堆用于显示请求的动态分配数据,通过malloc/free函数申请给种数据结构(链表、散列表、树等)的内存。
- 其他初始化任务,特别是输入输出相关的。例如,Unix系统中,默认情况下每个进程都有三个打开的文件描述符,用于标准输入、输出和错误。 0 1 2那几个fd。
以上任务完成后,启动程序,在入口处运行,即main()。通过跳转到main()例程,OS将CPU的控制权转移到新创建的进程中,从而开始执行程序。
进程状态
- 运行:正在执行指令
- 就绪:进程已准备好,但由于某种原因还不能运行
阻塞:进程执行了某种操作,直到发生其他事件时才会准备运行。例如:当进程向磁盘发起I/O请求时,它会被阻塞,因此其他进程可以使用处理器。
进程转换.png
看个例子,两个进程如何通过这些状态转换;
---------跟踪进程状态:只看CPU-----------
![b3cfc6aaf11f1d66a375da63c29aa2ec.png](https://i-blog.csdnimg.cn/blog_migrate/99d81e0b7fcf07bcc8e8ca666c780dd6.png)
下一个例子中,第一个进程在运行一段时间后发起I/O请求。此时该进程被阻塞,让另一个进程有机会运行。 --------跟踪进程状态:CPU和I/O-----------
![c9a687c3031de880ea4e9741639fa902.png](https://i-blog.csdnimg.cn/blog_migrate/310f0f07a9833ffd1e2060d3966d0d54.jpeg)
数据结构
为了跟踪每个进程的状态,操作系统可能会为所有就绪的进程保留某种进程列表,以及跟踪当前正在运行的进程的一些附加信息。操作系统还必须以某种方式跟踪被阻塞的进程。当I/O事件完成时,操作系统会确保唤醒正确的进程,让他准备好在此运行。
对于停止的进程,寄存器上下文将保存其寄存器的内容。当一个进程停止时,它的寄存器将被保存到这个内存位置。通过恢复这些寄存器(它们的值放回实际的物理寄存器中),操作系统可以恢复运行该进程。它被称之为 上下文切换
相关数据结构
//the registers xv6 will save and restore
//the stop and subsequently restart a process
struct context{............};
//the different states a process can be in
enum proc_state{...........};
//the information xv6 tacks about each process
// 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)
};
除了以上三种状态,还有其他状态,初始状态表示进程在创建时处于的状态。一个进程已退出但尚未清理,成为僵尸状态。这个状态可以允许其他进程(通常是创建进程的父进程)检查进程的返回代码,并查看刚刚完成的进程是否成功执行(Unix下程序运行成功返回0,否则返回非0)。完成后,父进程进行最后一次系统调用(如wait()),以等待子进程的完成,并告诉操作系统它可以清理这个正在结束的进程的所有相关数据结构。