【笔记】操作系统(三)——进程

前言

目前进入到了操作系统第一个重要的部分——进程管理。共有五个章节:进程、线程、CPU调度、进程同步、死锁。

这是第一个章节——进程的笔记。

一、进程的概念

进程(process)

进程,即运行中的程序。程序代码有时称为文本段或代码段。进程还包括当前活动,通过程序计数器的值和处理器寄存器的内容来表示。另外,进程通常还包括进程堆栈段(包括临时数据,如函数参数返回地址局部变量)和数据段(包括全局变量)。进程还可能包括堆,是在进程运行期间动态分配的内存。
在这里插入图片描述
概念提醒1: 程序本身不是进程,程序只是被动实体。如存储在磁盘上包含一系列指令的文件内容(常被称为可执行文件),而进程为活动实体,进程有一个程序计数器用来表示下一个要执行的命令相关资源集合当一个可执行文件被装入内存时,一个程序才能成为进程。

概念提醒2: “一个程序不能两次属于同一个进程”。对于同一个QQ,我们可以挂两个QQ号,这两个QQ都是独立的进程,虽然文本段(代码段)相同,但是数据段、堆、堆栈段却是不同的。

进程状态(process state)

进程在执行时会改变状态,共有五个状态:新的、就绪、运行、等待、终止。对于单个处理器,一次只有一个进程可以在处理器运行,但是多个进程可处于就绪或等待状态。
在这里插入图片描述

进程控制块(process control block,PCB)

PCB: PCB是系统为了管理进程设置的一个专门的数据结构,用它记录进程的外部特征,描述进程的运动变化过程。每个进程在操作系统内用进程控制块来表示,一个程序从被动实体转换为活动实体后,肯定要有控制它的数据结构。PCB是进程存在的唯一标志。 操作系统通过PCB来感知进程的存在。

进程控制块组成:
在这里插入图片描述

  • 进程状态:新的、就绪、运行、等待、终止。
  • 程序计数器:计数器表示进程要执行的下个指令的地址。
  • CPU寄存器根据计算机体系结构的不同,寄存器的数量和类型也不同。 它们包括累加器、索引寄存器、堆栈指针、通用寄存器和其它条件码信息寄存器。与程序计数器一起,这些状态信息在出现中断时也需要保存,以便进程以后可以正确地继续执行。
  • CPU调度信息:这类信息包括进程优先级、调度队列的指针和其他调度参数。
  • 内存管理信息:根据不同的内存系统,这类信息包括基址和界限寄存器的值、页表和段表。
  • 记账信息:这类信息包括CPU时间、实际使用时间、时间界限、记账数据、作业进程数量等。
  • I/O信息:包括分配给进程的I/O进程设备列表、打开的文件列表等。

总之,PCB是这些信息的仓库,这些信息在进程与进程之间是不同的。

CPU在进程间的切换:
在这里插入图片描述

二、进程调度

调度队列(Scheduling Queue)

调度队列主要有三类:作业队列、就绪队列、设备队列。调度队列通常用链表来实现,其头节点指向链表的第一个和最后一个PCB块的指针。

作业队列: 作业队列包括系统中的所有进程。

就绪队列: 驻留在内存中就绪的、等待运行的进程保存在就绪队列中。

设备队列: 假设进程向一个共享设备(磁盘、打印机等)发送I/O请求,由于系统有许多进程,设备可能会忙于其他进程的I/O请求,因此该进程可能需要等待磁盘。每个设备都有自己的设备队列。

就绪队列和设备队列如图:
在这里插入图片描述

队列图(讨论进程调度的常用表示方法):
在这里插入图片描述
上述队列图说明了新进程进入就绪队列后,被分配CPU并执行,可能发生的五种情况:

  1. 在时间片内执行完毕,进程终止,该进程被从所有队列中删除,其PCB和资源将得以释放。
  2. 在时间片内未执行完毕,进程停顿并重新进入就绪队列,等待下一次分配CPU。
  3. 进程发出一个I/O请求,并被放入相应设备I/O队列中。
  4. 进程创建了一个新的子进程,并等待其结束。
  5. 进程由于中断而强制释放CPU,并被放回到就绪队列中。

调度程序(Scheduler)

调度程序: 进程在其生命周期中会在各种调度队列之间迁移。为了调度,操作系统必须按某种方式从这些队列中选择进程。进程选择是由相应的调度程序来执行的。

调度程序的分类:

  • 长期调度程序:也叫做作业调度程序,从磁盘(也叫做缓冲池)中选择进程,并装入内存以准备执行。绝大多数进程可分为I/O为主或CPU为主,长期调度程序在选择进程时,为了达到最好性能,需要调度一个合理的I/O为主和CPU为主的组合进程。
  • 短期调度程序:也叫做CPU调度程序,从准备执行的进程(已经在内存中)中选择进程,并为之分配CPU。

上下文切换(Context Switch)

上下文切换: 将CPU切换到另一个进程需要保存当前进程的状态并恢复另一个进程的状态,这一任务称为上下文切换。上下文切换速度因机器而不同,它依赖于内存速度、必须复制的寄存器数量、是否有特殊指令,一般需要几毫秒。

三、进程操作

绝大多数系统内的进程能并发执行,它们可以动态创建和删除,因此操作系统必须提供某中机制以创建或终止进程。

进程创建

进程在其执行过程中,能通过**创建进程系统调用(create-process system call)**创建多个新进程。创建进程称为父进程,而新进程称为子进程。每个新进程可以再创建其他进程,从而形成了进程树。

大多数操作系统根据一个唯一的进程标识符(process identifier, pid)来识别进程,pid通常是一个整数值。

Solaris进程树:
在这里插入图片描述
当进程创建新进程时,有两种执行可能:

  • 父进程与子进程并发执行。
  • 父进程等待,直到某个或全部子进程执行完。

新进程的地址空间也有两种可能:

  • 子进程是父进程的复制品(具有与父进程相同的程序和数据)。
  • 子进程装入另一个新程序。

对于UNIX系统: 对于新进程,系统调用fork()的返回值为0;对于父进程,返回值为子进程的进程大于零;出现错误,返回小于零的值。如图:

在这里插入图片描述

进程终止

当进程完成执行最后的语句并使用系统调用exit()请求操作系统删除自身时,进程终止。这时,进程可以返回状态值(通常为整数)到父进程(通过系统调用wait())。所有进程资源会被操作系统释放。另一种情况,在子进程结束前,父进程已经结束或是没有wait()语句等待子进程结束,这时子进程就不可能被父进程处理,需要操作系统出面解决问题。不同的系统有不同的处理方式,有的系统不允许子进程在没有父进程的情况下继续执行,而Linux和UNIX会交由1号进程(init())作为父进程回收这类子进程。

四、进程间通信

操作系统内并发执行的进程可以是独立进程或协作进程。如果一个进程与其它进程不互相影响,那么该进程是独立的。另一方面,如果系统中一个进程能影响其他进程或被其他进程所影响,那么该进程是合作的。

为什么进程之间需要通信?

  1. 信息共享(information sharing): 多个用户可能对同样的信息感兴趣。
  2. 提高运算速度(computation speedup): 计算机如果有多个处理单元,子任务并行执行提高运算速度。
  3. 模块化(modularity): 可能需要按模块化方式构造系统。
  4. 便捷(convenience): 单个用户可能同时执行多个任务。

进程之间如何通信?

进程间通信机制(interprocess communication, IPC)两种方式:共享内存和消息传递
在这里插入图片描述
在这一部分,我们常常引用生产者、消费者这个例子。以及send()和receive()两个原语。

共享内存
  • 采用共享内存的进程间通信需要进程建立共享内存区域。
  • 一块共享内存区域驻留在生成共享内存段进程的地址空间。
  • 其他希望使用这个共享内存段进行通信的进程必须将此放到它们自己的地址空间上。
消息传递
直接或间接通信

直接通信: 需要通信的每个进程必须明确地命名通信的接收者或发送者。

  • send(P, message): 发送消息到进程P
  • receive(Q,message):接收来自进程Q的消息

间接通信: 通过邮箱或接口来发送和接收消息。

  • send(A, message): 发送一个消息到邮箱A。
  • receive(A, message): 接收来自邮箱A的消息。
同步或异步通信

消息传递可以是阻塞(同步)或非阻塞(异步)。

  • 阻塞send:发送进程阻塞,直到消息被接收进程或邮箱所接收。
  • 非阻塞send:发送进程发送消息并再继续操作。
  • 阻塞receive: 接收者阻塞,直到消息可用。
  • 非阻塞receive: 接收者收到一个有效消息或空消息。

以上的send()和receive()可以自由组合,形成不同的通信方式。

缓冲

不管通信是直接的还是间接的,通信进程所交换的消息都驻留在临时队列中。队列实现有三种方法:

  1. **零容量:**队列的最大长度为0。因此,线路中不能有任何消息处于等待,所以必须阻塞发送,直到接收者接收到消息。
  2. **有限容量:**队列的长度有限。因此,如果临时队列未满,发送者可继续执行。若临时队列已满,则必须阻塞发送者直到队列中的空间可用为止。
  3. 无限容量: 队列长度无限,多少消息都可以在其中等待。

六、客户机-服务器系统通信

Socket(套接字)

Socket可定义为通信的端点。一对通过网络进行通信的进程需要使用一对Socket——即每个进程各有一个。Socket由IP地址与一个端口号连接来完成。通常,Socket采用客户机-服务器架构。 服务器通过监听指定端口来等待进来的客户请求。一旦收到请求,服务器就接受来自客户Socket的连接,从而完成连接。

服务器实现的特定服务是通过监听公认的端口约定来进行的。比如,telnet服务器监听端口23,ftp服务器监听端口21,Web或http服务器监听端口80)。所有低于1024的服务器端口都被纳入公认的端口约定,可以用他们来实现标准服务。 所以当客户机发出连接请求时,往往被主机赋予一个大于1024的端口。

在这里插入图片描述
Socket的连接是唯一的,如上图所示,如果主机X的另一个进程希望与同样的Web服务器建立另一个连接,那么它会被分配另一个大于1024但不等于1625的端口号。

以JAVASocket为例: Java提供了三种不同类型的Socket。TCP、UDP以及多点传送Socket。

Socket的缺点: Socket虽然非常常用和高效,但是它属于较为低级的分布式进程通信。原因之一在于Socket只允许在通信线程之间交换无结构的字节流。客户机或服务器程序需要负责加上数据结构。

远程过程调用(RPC)

与IPC(进程间通信)工具不同,用于RPC交换的消息有很好的结构,因此不再仅仅是数据包。每个消息传递给位于远程系统上监听端口号的RPC服务器,每个都包含要执行函数的名称和传递给函数的参数。

RPC语义允许客户机调用位于远程主机上的过程,就如同调用本地过程一样。通过在客户端提供存根(stub)。RPC系统隐藏了允许通信发生的必要细节。

远程方法调用(remote method invocation, RMI)

RMI是RPC的Java版。RMI允许线程如同调用本地对象一样来调用远程对象的方法。RPC和RMI的主要区别是RPC传递给远程过程的数据是按普通数据结构形式的,而RMI允许把对象传递给远程方法。

猜你喜欢

转载自blog.csdn.net/qq_41882686/article/details/112425522
今日推荐