> 原文地址:https://sunnyqjm.github.io/2018/06/26/os_01/
进程
- 进程是程序在特定输入下的一次执行
- 是现代分时系统的工作单元,是系统进行资源分配和调度的基本单位
进程 是执行中的程序,不只是程序代码 ( 程序代码也称为文本段/代码段 ),还包括:
- 当前的活动:通过 程序计数器的值 以及 寄存器的内容 表示 => 当前正在执行哪一条指令处理什么数据
- 堆栈端:包括临时数据。如函数参数、返回地址和局部变量
- 数据段:包括全局变量
- 堆:进程运行期间动态分配的内存
进程状态
[图片上传失败…(image-82069c-1531380014724)]
- new (新建状态): 进程正在被创建
- ready (就绪状态): 进程等待被分配CPU
- running (运行状态): 指令正在被执行
- waiting (等待状态): 进程等待某个事件的发生(如I/O完成或收到信号)
- terminated (终止状态): 进程完成执行
进程控制块 (PCB)
- PCB是操作系统用来管理进程的唯一控制数据结构
- 每一个进程在操作系统内都用一个PCB数据结构表示
- PCB : Process Control Block
[图片上传失败…(image-58ad95-1531380014724)]
进程调度
进程调度选择一个可用的进程(可能从多个可用进程集合中选择)到CPU上执行
为何要进行进程调度?
在多道程序设计当中,由 多个进程竞争使用有限的CPU资源,如何合理的分配CPU资源便是进程调度要解决的问题。进程调度的目标是 => 使得CPU的利用率最大化
[图片上传失败…(image-b5c6d-1531380014724)]调度队列
- #### 作业队列
该队列包含系统中所有的进程(包括在内存中的、被交换到外存的以及在外存缓冲池当中的进程) - #### 就绪队列
该队列保存有驻留在内存中的就绪的、等待被分配CPU的进程 - #### 设备队列
等待特定I/O设备的进程列表称为 设备队列,每个设备都有自己的设备队列。
- #### 作业队列
调度程序
进程在其生命周期中会在各种调度队列之间迁移,而操作系统通过执行调度程序来选择队列中的进程进行相应的迁移。
参考:https://blog.csdn.net/u013007900/article/details/50550415
[图片上传失败…(image-795239-1531380014724)]
长期调度程序(long-term scheduler)
长期调度 又称为 作业调度 或 高级调度,这种调度将已进入系统并处于后备状态的作业按某种算法选择一个或一批装载入内存准备执行。
长期调度程序执行的并 不频繁
常用于批处理系统,在分时系统和实时系统中通常不需要长期调度
通过长期调度程序可以控制多道程序度 (内存中的进程数量)=> 在合适的时机从外存装载进内存(比如可以在一个进程执行完时),从而使得 创建进程的平均速度等于进程离开系统的平均速度,使得多道程序度达到稳定状态
在进行长期调度的时候,应该选择一个合理的包含I/O为主的和CPU为主的组合进程。(如果都是以I/O为主,那么可能就绪队列几乎为空,都在等待队列中等待I/O的结束,这使得短期调度无进程可调,CPU也没有得到充分的利用)
短期调度程序(short-term scheduler)
CPU调度算法:后续补充
短期调度 又称为 CPU调度 或 低级调度,它的主要任务是在就绪队列中选择一个进程,为其分配CPU,让其进入运行态开始执行。
短期调度程序执行的 非常频繁
常说的进程调度指的就是短期调度,其可分为 抢占式和非抢占式
中期调度程序(medium-term scheduler)
中期调度 又称为 交换调度,其核心思想是 将进程从内存(或从CPU竞争)中移出,从而降低多道程序度
- 进程被移出内存后,在CPU相对空闲时又能被重新调入内存,并从中断处继续执行。这种方案称之为 交换(swapping)
- 为了改善进程组合,或者因内存要求的改变引起了内存的过度使用而需要释放内存,就有必要使用交换。
上下文切换
将CPU切换到另一个进程需要 保存当前进程的状态并回复另一个进程的状态,这一任务称为 上下文切换(context switch)
当发生上下文切换时,内核会将旧进程的状态保存在其PCB中,然后装入经调度要执行的并已保存的新进程的上下文
上下文切换时间是额外的开销
上下文切换发生的时机:正在运行的进程被中断、进程运行结束、时间片到期等
进程操作
本小结介绍进程的创建和如何终止进程,代码示例则以Unix平台为例
进程创建
在进程执行的过程当中,可以通过 调用创建进程的系统调用 创建多个进程。创建进程称为 父进程,新创建的进程称为子进程。每个新进程又可以再创建其它进程,从而形成进程树。
进程标识符 (pid, process identifier)
大多数操作系统根据一个 pid 来 唯一标识一个进程,pid通常是一个整数值。
创建子进程后,父进程的行为
父进程与子进并发执行
父进程等待,直到某个或全部子进程执行完毕
新创建进程的地址空间也有两种可能
子进程是父进程的复制品 => 具有与父进程完全相同的程序和数据
子进程装入另一个新程序
Unix创建进程相关的命令
fork => 创建一个新进程,详细介绍可参考我的另一篇博客
exec => 实际上代表的是一个函数族,共有六个函数,主要用于在进程中调用其它的可执行程序。详细介绍可参考我的另一篇博客
wait => wait函数可用于等待子进程的结束(其主要功能还是用于清理僵死进程),详细接收可参考我的另一篇博客
举个创建子进程的栗子
如果子进程创建成功,则fork函数会返回两次,在子进程中返回0,在父进程中返回子进程id
#include <stdio.h> #include <sys/unistd.h> #include <sys/types.h> int main() { pid_t pid; //创建一个新进程 pid = fork(); if(pid < 0) { //error occurred fprintf(stderr, "Fork failed"); exit(-1); } else if(pid == 0) { //child process execlp("/bin/ls", "ls", NULL); } else { //parent process //parent will wait for the child to complete wait(NULL); printf("Child Completed\n"); exit(0); } return 0; }
- 9 : 创建一个进程并记录fork()函数的返回值
- 11-13: 如果pid < 0 表示创建进程失败,打印出错误提示并退出程序
- 14-15: pid == 0,表示当前进程是子进程,执行一个exec函数调用系统的ls命令查看当前文件夹的信息
16-20: pid > 0,表示当前进程是父进程,调用wait等待子进程执行结束,接着打印子进程结束信息,退出程序。
[图片上传失败…(image-6cd85b-1531380014724)]进程终止
进程可以在执行的过程中,调用 exit 系统调用来要求操作系统终止自身。当然,通过适当的系统调用,一个进程也可以终止另一个进程(比如Unix中常用kill命令杀死其它进程)
父进程终止子进程的原因
子进程使用了超过它所分配到的一些资源。(为了判定是否发生这种情况,要求父进程有一个检查子进程状态的机制)
分配给子进程的任务已不再需要
父进程退出,操作系统不允许子进程继续(对有些操作系统而言)
父进程退出时子进程如何处置?
有些操作系统,包括VMS,不允许子进程在父进程已终止的情况下存在。对于这类操作系统,如果一个进程终止,那么它的所有子进程都将终止。这种现象称之为 级联终止(cascading termination)
而对于常见的一些操作系统,如Unix,如果一个进程终止,则其所有的子进程并不终止,而是将其托管给init进程,init进程便是它的新父进程。
进程间通信
如果一个进程不被其他进程影响,也不影响其它进程,则称这个进程是独立的;如果一个进程能影响其他进程或被其它进程影响,则称该进程是协作的。=>进程间进行协作需要一种进程间通信机制(interprocess communication, IPC)
为什么进程间要进行协作?
- 信息共享(information sharing)=> 多个进程可以并发的访问同一块数据
- 提高运算速度(computation speedup)=> 将一个任务分为若干个子任务交由多个进程执行,可以提高运算速度
- 模块化(modularity)=> 一个系统可能被设计为多个模块,而每个模块各自在一个进程中运行,那么这些模块间肯定需要进行协同,这就要求进程间要能够进行通信。
- 方便(convenience)
进程间通信的方式
[图片上传失败…(image-d06264-1531380014724)]
共享内存
建立一块共享内存区域,对于需要访问共享内存区域的两个或多个进程需要打破操作系统对一个进程不能访问另一个进程的内存空间的限制。
可用来解决生产者消费者问题
可以采用有限缓冲和无限缓冲两种模式
只有在建立共享内存区域的时候需要内核干预,在具体通信的时候都是对内存的访问,可以不需要内核干预。可以以较大的速率进行同行。
消息传递
进程间通过互相发送消息来直接或间接的通信。
- ##### 直接通信或间接通信
- 直接通信:通信进程之间直接建立通信线路,互相发送消息来进行通信
- 间接通信:需要一个邮箱作为中转,进程均直接与邮箱进行通信,向邮箱发送消息或者从邮箱接收消息
- ##### 同步或异步通信
- 同步通信:消息的发送和接收是阻塞执行的。直到发送的消息被成功接收或者成功接收到消息,才会继续执行之后的操作
- 异步通信:消息发送和接收是异步的。对于发送操作,消息发出后不关心是否被接收,直接执行之后的操作;对于接收操作,每次接收,均一个有效的消息或者是一个空消息,然后继续执行之后的操作。
缓冲
不管通信是直接的还是间接的,通信进程所交换的消息都驻留在临时队列当中。简单讲,队列有以下三种实现方式:
- 零容量:缓冲队列为0
- 有限容量:缓冲队列为n
- 无线容量:缓冲队列无限大
零容量的情况称为没有缓冲的系统,其它情况称为自动缓冲