操作系统-进程
进程不只是执行中的程序代码,还包括当前活动(通过程序计数器的值和处理器寄存器的内容来表示)、进程堆栈段(包括临时数据如函数参数、返回地址和局部变量)、数据段(包括全局变量)、堆(在进程运行期间动态分配的内存)。
内存中的进程结构:
程序不是进程,程序是存储在磁盘上的包含一系列指令的文件内容(被动实体),而进程是有一个程序计数器用于表示下一个要执行的命令和相关资源集合(活动实体)。
多个进程可以与同一程序相关,但进程之间相互独立。
PCB:每个进程在操作系统内用进程控制块(process control block,PCB,也称为任务控制块)来表示。
进程状态:新建、运行、等待、就绪、终止。
一次只有一个进程可以再一个处理器上运行,但有多个进程可处于就绪或等待状态。
程序计数器:表示进程要执行的下个指令的地址。
CPU寄存器:包括累加器、索引寄存器、堆栈指针、通用寄存器和其他条件码信息寄存器,根据不同的计算机体系结构有不同的类型和数量。
CPU调度信息:包括进程优先级、带哦度队列的指针和其他调度参数。
内存管理信息:包括基址和界限寄存器的值、页表或段表。
记账信息:包括CPU时间、实际使用时间、时间界限、记账数据、作业或进程数量等。
I/O状态信息:包括分配给进程的I/O设备列表、打开的文件列表等。
出现中断时,需要保存程序计数器和CPU寄存器的状态信息。
进程调度:从多个可用进程集合中选择一个可用的进程到CPU上执行。
Linux操作系统的PCB通过C结构task_struct表示,内核中所有活动进程由task_struct的双向链表表示,为当前正在运行的进程保留一个指针current。
作业队列:包括系统中的所有进程,进程进入系统时会被加入到作业队列。
设备队列:等待特定I/O设备的进程列表,每个设备都有自己的设备队列。
就绪队列:包括等待运行的进程。通常用链表实现,头结点指向链表的第一个和最后一个PCB块的指针,每个PCB包括一个指向就绪队列的下一个PCB的指针域。
队列图:讨论进程调度的常用表示方法,长方形表示队列(就绪队列和设备队列),圆形表示资源,箭头表示系统内进程流向。
新进程开始于就绪队列,在就绪队列中等待直到被选中执行或派遣;
进程分配到CPU并执行时,可能发生的几种事件:
·发出I/O请求并被放到I/O队列中
·创建一个新的子进程并等待其结束
·由于中断而强制释放CPU并被放回到就绪队列
进程终止时将从所有队列中删除,释放PCB和资源。
调度程序(scheduler):执行按某种方式从调度队列中选择进程的程序。
长期调度程序(long-term scheduler或作业调度程序job scheduler):批处理系统中进程提交后通常被放到大容量存储设备(磁盘)的缓冲池中保存以便以后执行,长期调度程序从该池中选择进程并装入内存,以准备执行。
长期调度程序控制多道程序设计的程度(内存中的进程数量);
长期调度程序受资源分配考虑,尤其是内存管理的影响;
长期调度程序选择的进程主要包括I/O为主的进程(I/O-bound process)和CPU为主的进程(CPU-bound process);
I/O为主的进程时间主要花费在执行I/O方面,与等待队列有关;
CPU为主的进程时间主要花费在执行计算方面,与就绪队列有关;
应合理选择一个包含I/O为主进程和CPU为主进程的组合,使系统平衡。
短期调度程序(short-term scheduler或CPU调度程序):从准备执行的进程(就绪队列)中选择进程,并为之分配CPU。
主要差别:执行频率。短期调度程序执行频繁,通常每100ms执行一次;长期调度程序执行不频繁,可能有数分钟间隔。
中期调度程序(medium-term scheduler):用交换(swapping)的手段,将进程从内存中换出,降低多道程序设计的程度。之后重新换入内存,并从中断处继续执行。
上下文切换(context switch):将CPU切换到另一个进程时保存当前进程状态并恢复另一个进程的状态;
发生中断时,内核保存旧进程的上下文(以便下次恢复),装入经调度需要执行并已保存的新进程的上下文;
进程上下文用进程的PCB表示;
上下文切换时间为额外开销,与硬件支持有关;
pid:大多数操作系统根据一个唯一的进程标识符(process identifier,pid)识别进程,通常是一个整数值。
操作系统必须为父进程创建子进程提供机制,父进程在继续之前可以等待子进程终止,也可以并发执行;
并发执行的优点:信息共享、运算速度、模块化和便利性等;
子进程可能从操作系统直接获得资源,也可能只从父进程获得资源;
父进程可能必须在其子进程之间分配资源,限制子进程只能使用父进程资源从而防止系统超载;
操作系统内并发执行的进程可以是独立进程或协作进程;
独立进程:不能影响其他进程或被其他进程影响;
协作进程:能影响其他进程或被其他进程影响;
IPC(进程间通信机制,interprocess communication):允许协作进程相互交换数据与信息的机制,有两种基本模式(1)共享内存(2)消息传递;
共享内存模式:建立一块供协作进程共享的内存区域,进程通过向共享区域读写数据交换信息;(速度较快,仅在建立共享内存区时需要系统调用,通信时常规内存访问不需要来自内核的帮助)
消息传递模式:通过在协作进程间交换消息来实现通信;(优点:不需要避免冲突,适合交换少量数据;对计算机间通信更易于实现)(缺点:系统调用频繁,需要更多内核介入的时间消耗)
共享内存要求通信进程共享一些变量来交换信息,主要由应用程序员提供通信,操作系统只需提供共享内存;
共享内存解决生产者-消费者问题:为允许生产者进程与消费者进程并发执行,必须要有一个缓冲驻留在生产者进程和消费者进程的共享内存区域内,供生产者填充并被消费者使用。当消费者使用一项时,生产者能生产另一项;生产者和消费者必须同步,以免消费者消费一个未生产出来的项;
无限缓冲(unbounded-buffer):对缓冲大小没有限制,消费者可能不得不等待新项,但生产者总是可以产生新项;
有限缓冲(bounded-buffer):缓冲大小固定,如果缓冲为空消费者必须等待,如果缓冲为满,生产者必须等待;
有限缓冲的使用(该方法允许最大缓冲项数BUFFER_SIZE-1):
int in=0, out=0;
逻辑指针变量in指向缓冲中下一个空位,out指向缓冲中第一个满位;
(in+1)%BUFFER_SIZE == out时缓冲为满;
生产者进程用局部变量nextProduced存储产生的新项:
while (true) { //produce an item in nextProduced while (((in+1)%BUFFER_SIZE) == out) ; buffer[in] = nextProduced; in = (in+1)%BUFFER_SIZE; }
消费者进程用局部变量nextConsumed存储要使用的新项:
while (true) { while (in == out) ; nextConsumed = buffer[out]; out = (out+1)%BUFFER_SIZE; //consume the item in nextConsumed }
消息传递系统
消息传递系统允许进程交换信息,提供通信责任在于操作系统本身;
进程间通过消息传递通信必须要有通信线路(communication link),逻辑实现线路包括直接或间接通信、同步或异步通信、自动或显示缓冲等;
直接通信:需要通信的每个进程必须明确命名通信的接收者或发送者(对称寻址);
原语定义:send(P, message); receive(Q, message);
直接通信线路具有属性:
1. 需要通信的每对进程之间自动建立线路,仅需知道相互通信的标识符;
2. 一个线路只与两个进程相关;
3. 每对线程之间只有一个线路;
变形:非对称寻址,只要发送者命名接收者,接收来自任何进程的消息;
原语定义:send(P, message); receive(id, message);
对称/非对称寻址的缺点:限制了进程定义的模块化,改变进程的名称可能必须检查所有其他进程定义。
间接通信:通过端口或邮箱(一个抽象对象)发送和接收消息,进程可以向邮箱中存放/删除消息,每个邮箱都有一个唯一的标识符;
原语定义:send(mailbox, message); receive(mailbox, message);
间接通信线路属性:
1. 两个进程至少共享一个邮箱时才能通信;
2. 一个线路可以与两个或更多进程关联;
3. 两个通信进程间可以有多个不同线路,每个线路对应一个邮箱;
进程或操作系统可以拥有邮箱;
如果邮箱为进程所有,需要区分为进程所有,需要区分拥有者(通过邮箱接收消息)和使用者(向邮箱发送消息),当拥有邮箱的进程终止时邮箱消失;
邮箱的拥有者默认为创建邮箱的进程,但通过系统调用,拥有权和接收权可能传递给其他进程;
由操作系统拥有的邮箱是独立存在的,不属于某个特定进程,操作系统需提供机制允许创建/删除邮箱和通过邮箱发送/接收消息;
阻塞send:发送进程阻塞,直到消息被接收进程或邮箱接收
非阻塞send:发送进程发送消息并继续操作
阻塞receive:接收者阻塞,直到有消息可用
非阻塞receive:接收者收到一个有效消息或空消息
send()和receive()可进行多种组合
无论直接或间接通信进程,交换的信息都驻留在临时队列中,队列实现有三种方法:
1. 零容量:队列最大长度为0,线路中不能有任何消息处于等待状态,必须阻塞发送,直到接收者接收到消息。
2. 有限容量:队列长度为有限的n,最多只能有n个消息驻留。发送消息时如果队列未满,消息可存放在队列中,发送者可继续执行;如果队列满,必须阻塞发送,直到队列中空间可用。
3. 无限容量:队列长度可以无限,从不阻塞发送。
零容量称为没有缓冲的消息系统,其他情况称为自动缓冲。
共享内存和消息传递也可用于客户机-服务器系统通信,此外,客户机-服务器系统通信还有三种通信方法:Socket、远程过程调用(RPC)、Java远程发发调用(RMI);
Socket:通信的端点,由IP地址和端口号连接组成,一对通信进程使用一对Socket,每端各有一个通信频道;
低于1024的服务器端口用于实现标准服务,客户机发出连接请求时赋予大于1024的端口号;
服务器监听端口,收到客户请求,接受来自客户端Socket的连接,建立连接传输数据包;
所有的连接都有唯一的一对Socket;
Socket通信常用高效,但只允许在通信线程之间交换无结构的字节流,属于较为低级的分布式进程通信。
RPC(远程过程调用):用于网络及连接系统,基于(IPC)消息通信方案提供远程服务;
与IPC不同,交换的消息有很好的数据结构,不仅仅是数据包;
消息传递给远程系统上监听端口号的RPC服务器,返回结果通过另一个消息传递给请求者;
每个独立的远程过程都有一个stub(存根),RPC系统在客户端提供stub;
客户端调用远程过程(调用远程应用的方法)àRPC调用合适的stub并传递相应参数àstub定位服务器的端口并编组(marshal)参数àstub传递消息(以消息传递的方式)给服务器à服务器端一个相似的stub接收消息并调用服务器端的过程à必要时返回值传回给客户端;
许多RPC系统都定义了数据的及其无关标书,如外部数据表示(XDR),用于处理客户机和服务器系统数据表示差别问题;
由于普通网络错误可能会导致RPC重复多次执行,处理方法是操作系统确保一个消息刚好执行一次;
首先通过为每个消息附加时间戳,服务器通过时间戳历史检验重复消息保证消息最多执行一次;
在最多一次协议基础上,客户机周期性重发RPC调用直到接收到该调用的ACK,确保客户端已经接收到RPC并已执行,保证消息执行刚好一次;
RPC方案中客户端需要知道与其绑定的服务器端口,有两种方法:
1. 绑定信息以固定端口地址的形式预先确定:编译时每个RPC调用都有一个固定端口号,编译后服务器不能改变所请求服务的端口号;
2. 通过集合点机制(rendezvous mechanism)动态绑定:操作系统在固定的RPC端口上提供集合点(也称为matchmaker)服务程序,客户端将包含RPC名称的消息发送给rendezvous,请求所需执行的RPC端口地址->rendezvous返回端口号(可一直将RPC调用发送给该端口,直到进程终止或服务器崩溃,该方法需要初始请求的额外开销,但更灵活)。
RMI:远程方法调用(remote method invocation),类似RPC的java特性,允许线程调用远程(位于另一JVM上的)对象的方法;
RMI与RPC的区别:
1. RMI基于对象,支持调用远程对象,RPC只能调用远程的子程序或函数;
2. RMI可将对象作为参数传递,而RPC参数是普通数据结构;
客户机调用远程方法的过程:
调用客户机中的存根(stub),创建包含服务器上要调用的方法名称和编排参数的包,发送给服务器端;
骨干(skeleton)接收stub发送的包,重新编排参数,调用服务器上所要执行的方法(方法的真正实现驻留在服务器上),方法完成后skeleton编排返回值(或异常),打包返回给客户端;
stub重新编排返回值,传递给客户机;