3.1 进程概念 P72
进程
进程概念:
- 进程是执行中的程序
进程包含:
- 文本段, 或程序代码
- 堆栈段 (包含临时数据,如方法参数、返回地址和局部变量)
- 数据段(包含全局变量)
- 寄存器
进程和程序的区别
- 进程是动态的,程序是静态的: 程序是有序代码的集合;进程是程序的执行。eg,程序是菜谱,那么进程是按菜谱炒菜的过程。
- 进程是暂时的,程序是永久的: 进程是一个状态变化的过程,程序可长久保存。
- 进程是系统分配调度的基本单位, 进程更能真实地描述并发,而程序不能。进程与程序的对应关系: 通过多次执行,一个程序可对应多个进程
进程状态
- 进程在执行时会改变状态。
- 每个进程可能处于下列状态之一:
- 新的: 进程正在被创建
- 运行: 指令正在被执行
- 等待: 进程等待一定事件的出现(如I/O完成或收到某个信号)
- 就绪: 进程等待被分配给某个处理器
- 终止: 进程已完成执行 - 状态的名称较随意,且随操作系统的不同而变化。
进程控制块(PCB)
- 每个进程在操作系统内用进程控制块 process control block (PCB) 来表示, 也称为任务控制块。
- PCB 包含了与特定进程相关的许多信息。
- PCB 驻留在内核空间。
PCB的内容
- 进程状态
- 程序计数器
- CPU 寄存器
- CPU 调度信息
- 内存管理信息
- 记帐信息
- I/O 状态信息
CPU 在进程间的切换图
从图中可以得到:
- 一个进程的生命周期是走走(executing)停停(idle)的,是在切换的。
- 当进程从执行转入之后,控制权是要交回操作系统的,操作系统在此阶段进行上下文切换,保存旧进程的现场,装入下一个进程的现场(状态信息) ,然后将控制权交给下一个进程,开始执行。
- 等待上下文切换要花时间的(下图的停顿时间),上下文切换时间短点好,此时CPU是闲着的。
3.2 进程调度 P75
- 多道程序设计的目的
- 无论任何时候都有进程在运行,从而使CPU利用率达到最大化 - 分时系统目的
- 在进程间频繁切换 CPU ,以便用户在程序运行时能与其交互 - 单处理器系统只能有一个运行进程
调度队列
- 作业队列 —— 系统中的所有进程
- 就绪队列 —— 驻留在内存中就绪的等待运行的进程
- 通常用链表形式来存储 - 设备队列 —— 等待特定I/O设备的进程列表, eg. 磁带,磁盘
- 每个设备都有自己的设备队列
就绪队列和各种I/O设备队列
- 等着分配CPU的进程有哪些,数量是多少?
就绪队列:PCB7,PCB2。等待分配CPU。
设备队列:PCB3,PCB14,PCB6。等待分配磁盘。
表示进程调度的队列图
- 进程在各种调度队列之间迁移
调度程序
- 长期调度程序 (或作业调度程序) —— 选择进程装入内存以执行。
- 短期调度程序 (或CPU调度程序) —— 从就绪队列中选择进程,为其分配CPU。
- 调度程序是操作系统的一部分。
- 短期调度程序执行非常频繁 (数毫秒,非常快)
- 长期调度程序执行得并不频繁 (数分钟间隔)
- 长期调度程序控制 多道程序设计的程度,即内存中的进程数量。
(内存中的进程数量越多,并发程度越高) - 进程可分为:
- I/O为主的进程 —— 在执行 I/O 方面比执行计算要花费更多的时间
- CPU为主的进程 —— 将更多的时间用在执行计算上 - 长期调度程序应选择一个合理的进程组合。
中期调度程序
- 交换 —— 将进程移出内存(并移出对CPU的激烈竞争),因此降低多道程序设计的程度。之后,进程能被重新调入内存,并从中断处继续执行。
(移除低优先级,移入高优先级进程)
上下文切换
- 进程关联是由进程的PCB来表示的
- 当发生上下文切换时,内核会将旧进程的关联状态保存在其PCB中,然后装入经调度要执行的新进程的已保存的关联状态.
- 上下文切换时间是额外开销,因为切换时系统并不能做什么有用的工作
- 上下文切换时间与硬件支持密切相关
- 内存速度, 寄存器的数量, 是否有特殊指令, 内存管理方法
3.3 进程操作 P79
- 进程创建
- 进程终止
进程创建
- 父进程 创建 子进程, 新进程的每一个可以再创建其它进程,从而形成了进程树。
一个典型的Solaris系统中的进程树
父进程和子进程
资源共享
- CPU 时间, 内存, 文件, I/O 设备。
资源共享三种情况:
- 父子进程共享所有资源
- 子进程从其父进程资源子集那里获得资源
- 父进程必须在其子进程之间分配资源或共享资源
- 父子进程不共享资源
执行两种情况:
- 父子进程并发执行
- 父进程等待,直到某个或全部子进程执行完毕
地址空间两种情况:
- 子进程是父进程的复制品 (UNIX)
- 子进程装入另一个程序进来 (DEC VMS)
- Windows NT 支持两种模式
UNIX
- fork 系统调用创建子进程
- fork系统调用有两个返回值。
- ① 大于0的整数,代表子进程的编号,返回给父进程。
- ② 0 ,返回给子进程。
- fork系统调用有两个返回值。
- exec 系统调用以新程序来取代进程的内存空间
创建另一个进程的C程序
fock()的作用:创建子进程,并有返回值。
#include <stdio.h>
void main( )
{ int x;
x = fork( ); // fork another process
if (x < 0) { // parent process, an error occurred
printf(“fork failed\n”);
exit(-1);
} else if (x > 0) { // parent process
wait(NULL); // wait for the child to complete
printf(“child completed\n”);
exit(0);
} else { // x==0, child process
execlp(“/bin/ls”, “ls”, NULL);
}
}
eg:父进程的编号是38,产生子进程的编号是39,但子进程拿到的返回值是0。返回给父进程的是子进程的编号。返回值小于0代表创建子进程失败,进程结束。调用getpid()可以知道自己的编号。子进程想知道父进程的编号用getppid()。
进程生成
进程终止
- 当进程完成执行最后的语句并使用系统调用exit请求操作系统删除它时,进程终止
- 进程可以返回数据到其父进程 (通过 wait )
- 操作系统释放进程资源 (memory, files, I/O buffers, etc.) - 通过系统调用(如 abort ),一个 进程(通常是父进程) 可以终止另一个进程 (子进程) ,原因如下:
- 子进程使用了超过它所分配到的一些资源
- 分配给子进程的任务已不再需要
- 父进程退出,os不允许子进程继续,级联 终止
进程终止举例
- UNIX系统
- 可以通过系统调用 exit( ) 终止进程
- 父进程通过系统调用 wait( ) 以等待子进程的终止
- wait( ) 返回了终止子进程的进程标识符
- 如果父进程终止,那么其所有子进程会以 init 进程作为它们新的父进程
3.4 进程间通信 P83
- 并发 进程可以是 独立进程 或 协作进程
- 独立进程: 一个进程不能影响或被在系统内执行的其他进程所影响
- 进程间不共享任何数据
- 协作进程: 一个进程能影响或被在系统内执行的其他进程所影响
- 与其它进程共享数据
进程协作的优点
- 信息共享
- 多个用户对同样的信息感兴趣 。
- 加快计算
- 将特定任务分为若干子任务 (多处理器或 I/O信道)。
- 模块化
- 将系统功能划分成独立进程或线程。
- 方便
- 单个用户也可能同时执行许多任务.例如,一个用户可能编辑、打印和并行编译。
进程间通信机制(interprocess communication,IPC)
- 消息传递 or 共享内存
进程通信—共享内存
-
进程协作的例子:生产者-消费者问题
- 生产者 进程产生信息,以供 消费者 进程消费
- e.g. 打印程序产生字符,以供打印机驱动程序使用
-
缓冲区用来被生产者填充,被消费者使用
- 如果缓冲区为空,消费者必须等待生产者生产
- 无限缓冲: 对缓冲区大小没有实际限制。
- 有限缓冲: 假设缓冲区大小固定 => 如果缓冲区为满,那么生产者必须等待。
无限缓冲
- in:生产者
- out:消费者
- 红色:满,蓝色:空。
- out最多跟in并排,代表缓冲区为空。
有限缓冲
- 生产者、消费者共享如下变量
#define BUFFER_SIZE 10
typedef struct {
. . .
} item;
item buffer[BUFFER_SIZE];
int in = 0; 1
int out = 0;
- 共享缓冲通过循环数组和两个逻辑指针来实现: in 和 out
- 当 in==out,缓冲区空
- 当 ((in+1)%BUFFER0-_SIZE)==out,缓冲区满
- 最多允许缓冲区同时有BUFFER_SIZE-1 个项
有限缓冲图例
蓝色为空,红色为满
初始情况下,12个格子都为蓝色(空)。
- in开始生产到5 out 到2
- 第二种情况在无线缓冲是不可能出现的,in在out后面;在有限缓冲是可能的,in绕到了out的后面,是个环。
- 用in == out来代表满,是无效的。
- in和out保持一个缓冲区的距离,(in+1) == out代表是满的
有限缓冲: 生产者
item nextProduced; // local variable
while (1) {
/* produce an item in nextProduced */
while (((in + 1) % BUFFER_SIZE) == out)
; /* buffer is full, do nothing */
buffer[in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
}
有限缓冲: 消费者
item nextConsumed; // local variable
while (1) {
while (in == out) // buffer is empty
; /* do nothing */
nextConsumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
/* consume the item in nextConsumed */
}
进程通信—消息传递
- 消息系统 —— 允许进程互相通信而不需要利用共享数据
- 在分布式环境中特别有用
- IPC工具至少提供两个操作:
- 发送 (消息), 消息可以是定长的或变长的
- 接收 (消息)
通信线路
- 如果 P和 Q 需要通信,那么:
- 要在它们之间建立 通信线路
- 通过 send 和 receive 原语
- 原语primitive:由若干条指令构成的“原子操作(atomic operation)”过程,作为一个整体而不可分割--要么全都完成,要么全都不做。许多系统调用就是原语。但系统调用并不都是原语。
- 通信线路实现方法
- 物理实现 (e.g. 共享内存、硬件总线或网络)
- 逻辑实现
- Linux: 管道, 套接字,信号, 消息队列
直接通信
- 必须明确的命名通信的接收者或发送者
- send (P, message) – 发送消息到进程 P
- receive (Q, message) – 接收来自进程 Q的消息
- 该通信线路属性
- 自动建立线路
- 一个线路只与两个进程相关
- 每对进程间只有一个线路
对称寻址和非对称寻址
直接通信中,发送和接受进程必须指定对方,以便通信。称为对称
- 对称
- send (P, message)
- receive(Q, message)
- 非对称
- send(P, message)
- receive(id, message), 接收来自任何进程的消息,变量id设置成与其通信的进程名称
- 直接通信的缺点:
- 限制了结果进程定义的模块化。改变进程的名称必须检查所有其它进程定义。
间接通信
- 消息通过邮箱或端口来发送和接收
- 每个邮箱都有一个唯一的标识符
- 两个进程通过共享一个邮箱来通信
- 间接通信原语定义如下:
- send(A, message) – 发送一个消息到邮箱A
- receive(A, message) – 接收来自邮箱A的消息
通信线路的属性
- 只要一对进程共享一个邮箱,就建立了线路
- 一个线路可以与两个或更多的进程相关联
- 两个通信进程之间可有多个不同的线路,每个线路对应于一个邮箱
共享邮箱
- P1 , P2 , 和 P3 共享邮箱 A
- P1 发送, P2 和P3 接收
- 谁能收到消息?
- 处理方案
- 允许一个线路最多只能与两个进程相关联
- 允许一次最多一个进程执行receive操作
- 允许系统随意选择一个进程以接收消息
邮箱所有权
为进程所有
- 邮箱是进程地址空间的一部分
- 拥有者能通过邮箱接收消息
- 使用者只能向邮箱发送消息
- 当拥有邮箱的进程终止,那么邮箱消失
为操作系统所有
- OS 必须提供机制,以允许进程进行如下操作
- 创建 一个新邮箱
- 发送 和 接收 消息
- = =删除== 一个邮箱
- 通过适当的系统调用,拥有权和接收权可能传递给其他进程
同步
- 消息传递可以是阻塞和非阻塞(同步或异步)
- 阻塞send: 发送进程阻塞,直到消息为接收进程或邮箱所接收
- 非阻塞send: 发送进程发送消息并再继续操作
- 阻塞receive: 接收者阻塞,直到有消息可用
- 非阻塞receive: 接收者收到一个有效消息或无效消息
- send 和 receive 的不同组合都有可能
缓冲
- 不管通信是直接的或是间接的,通信进程所交换的消息都驻留在临时队列中
队列实现有三种方法:
- 零容量: 队列的最大长度为0,发送者必须阻塞,直到接收者接收到消息
- ==有限容量:==队列的长度为有限的 n; 如果线路满,发送者必须阻塞直到队列中的空间可用为止
- 无限容量: 发送者从不阻塞
3.6 客户机-服务器系统通信 P93
- 一台机器的进程需要访问另一台机器的数据
- three ways:
- 套接字
- 远程过程调用Remote Procedure Calls (RPC)
- 远程方法调用Remote Method Invocation (Java)
套接字(Socket)
- 套接字可定义为通信的端点
- 套接字由IP地址和端口号连接组成
- 套接字 161.25.19.8:1625 指的是 主机 161.25.19.8 端口号 1625
- 一对通过网络通信的进程需要使用一对套接字
- 所有连接必须唯一
使用套接字通信
- 两种类型套接字
- TCP, 面向连接
- UDP, 无连接
- 套接字属于较为低层形式的分布式进程通信
- 只允许在通信线程之间交换无结构的字节流
- 普遍,高效
远程过程调用(RPC)
- 远程过程调用remote procedure call(RPC):抽象过程调用机制,用于通过网络进行连接的系统。
- RPC的通信模型
- Client端:
- 发送远程过程调用的消息(以消息包形式)给远程的server端;
- 等待, 直到收到server端对该请求的回复;
- 一旦接收到来自server端的返回执行结果, 就继续执行后面的程序.
- Server端:
- 倾听状态, 等待client端发送过程调用消息;
- 一旦接收到过程调用消息, server就抽取参数并分析它, 然后执行所请求的过程;
- 将执行结果以消息包形式回送给client.
-
RPC是把传统本地过程调用的概念加以扩充后引入分布式环境的一种形式
-
RPC的形式和行为与传统本地过程调用极为相似,差别仅在于被调用的 procedure(过程)实际运行在与调用者的场点不同的场点上
-
RPC的具体实现 (补充)
其中stub是一组RPC机制的操作原语, 这些原语构成了RPC的实现细节, 它可以独立于client、server编程.
- 调用者调用本地stub中的一个过程(开始远程过程调用请求).
- 这个stub过程把有关的参数组装成一个消息包或一组消息包, 形成一条消息. 运行此执行过程的远程场点的IP地址和执行该过程的进程ID号也包含在这条消息中.
- 将这条消息发送给对应的RPC runtime(RPC运行库)子程序, 由这个子程序将消息发送到远程场点.
- 在接收到这条消息时, server端的RPC runtime子程序引用与被调用者对应的stub中的一个子程序, 并让它来处理消息.
- 与被调用者对应的stub中的这个子程序撤卸消息, 解析出相关参数, 并用本地调用方式执行所指定的过程.
- 返回调用结果, 调用者对应的stub子程序执行return语句返回到用户, 整个RPC过程结束.
远程方法调用(RMI)
- 远程方法调用 (RMI) 是 Java 的一个类似 RPC的功能
- RMI 允许线程调用远程对象的方法
编排参数
使用参数A,B对 someMethod调用了远程对象的存根。存根将参数A,B以及要调用的方法名称一起打包,发送给服务器。骨干重新编排参数并调用方法 someMethod. someMethod的真正实现驻留在服务器上。方法完成后,骨干编排从someMethod返回的 boolean值,将该值发回给客户机
小结
- 进程是执行中的程序
- 随着进程的执行,它改变状态
- 就绪, 运行, 等待, …
- 进程由 ==PCB==来表示
- 进程队列
- 就绪队列, I/O 请求队列, …
- 长期 (作业) 和短期 (CPU) 调度
- 并发进程
- 独立进程
- 协作进程
- e.g. 生产者和消费者
- 进程通信
- 共享内存
- 消息传递