进程process pid 与线程(一)——进程

进程,是对正在运行的程序的一个抽象。

 

进程

 

进程由三部分组成

  1. 程序段
  2. 相关的数据段
  3. PCB ——进程控制块,即 PCB(Process Control Block)

 

CPU由一个进程快速切换至另一个进程,使得每个进程运行几十或几百毫秒,从而产生一种并行的错觉。

 

 

进程模型

 

一个进程就是一个正在执行程序的实例,包括程序计数器、寄存器和变量当前值。从概念上说,每个进程拥有自己的虚拟CPU。实际上真正的CPU在进程之间来回切换。这种快速的切换称作多道程序设计

 

由于CPU在各个进程之间来回快速切换,因此每个进程执行其运算的速度是不确定的。而且,当同一个进程再次运行时,其运算速度通常也是不可再现的。所以,在对进程编程时,决不能对时序做任何想当然的假设。

 

如果一个程序运行两遍,则算作两个进程。例如开启两个Word文件。像“两个进程恰好运行同一个程序”这样的事实,其实是无关紧要的。

 

 

 

进程的创建

 

四种会触发进程创建的事件

1、系统初始化

2、正在运行的程序执行了创建程序的系统调用。

3、用户请求创建一个新进程

4、一个批处理作业的初始化

 

守护进程(daemon):停留在后台处理诸如电子邮件、web网页之类活动的进程。

 

基于命令行的Unix系统中运行程序X,新的进程会从该进程接管开其他的窗口。

Windows系统中,多数情形是这样的,在一个进程开始时,他并没有窗口,但是他可以创建一个或多个窗口。

 

新进程的创建,都是由于一个已经存在的进程,执行了一个用于创建进程的系统调用而创建的。这个进程所做的工作就是,执行一个用来创建新进程的系统调用,这个系统调用通知操作系统创建一个新进程,并且直接或间接的制定该进程中运行的程序

 

进程创建之后,父进程和子进程有各自不同的地址空间。如果其中某个进程在其地址空间中修改了一个字,这个修改对其他进程而言是不可见的。进程之间,可写的内存是不可以共享的

 

 

 

进程的终止

 

触发进程终止的条件

1、正常退出(自愿)——例如Unix中调用exit来实现完成工作之后的进程的自动退出。

2、出错退出(自愿)——当进程发现了严重错误的时候,会自动退出,比如当用户键入编译某文件的命令,而该文件并不存在时。

3、严重错误(非自愿)——当进程内部引起错误的时候,会执行该退出。通常是有程序错误引起的,比如执行了一条非法指令、引用不存在的内存,或者除数是零等。

4、被其他进程杀死(非自愿)——kill命令

 

当一个进程终止时,不论是否是自愿的,该进程所创建的所有进程也一律被杀死。

 

 

 

进程的层次结构

 

进程只有一个父进程。

 

Unix系统中,进程和他创建的子进程以及后裔共同组成进程组。当用户从键盘发出信号时,该信号被送给当前与键盘相关的进程组中的所有成员。每个进程可以分别捕获该型号、忽略该信号或采取默认的动作,即被该信号杀死。

 

Windows系统中没有进程层次的概念,所有的进程地位相同。唯一类似于进程层次的按时是在创建进程的时候,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程。但是他有权把这个令牌传送给其他进程,这样就不存在层次了。在Unix系统中,进程就不能剥夺其子进程的“继承权”。

 

 

 

进程的状态

 

1、运行态——该时刻进程实际占用CPU

2、就绪态——可运行,但是因为其他进程在占用CPU,所以运行暂停

3、阻塞态——当进程正在等待某个资源的时候,如果获取到资源,则进程继续执行,否则,进程不能运行。

阻塞态有时也称为等待状态或封锁状态。致使进程阻塞的典型事件有:请求 I/O,申请缓冲空间等。

 

进程的三种状态有四种可能的转换关系。

 

进程的其他状态

  • 创建态——
  • 终止态——
  • 挂起态——

 

 

 

操作系统最底层是调度程序,在他上面有许多进程。所有关于中断处理、启动进程和停止进程的具体细节都被隐藏在调度程序中。

补充:挂起状态

一些系统中,又增加了一些新状态,最重要的是挂起状态。引入挂起状态的原因有:

(1) 终端用户的请求。当终端用户在自己的程序运行期间发现有可疑问题时,希望暂时使自己的程序静止下来。亦即,使正在执行的进程暂停执行;若此时用户进程正处于就绪状态而未执行,则该进程暂不接受调度,以便用户研究其执行情况或对程序进行修改。我们把这种静止状态称为挂起状态。

(2) 父进程请求。有时父进程希望挂起自己的某个子进程,以便考查和修改该子进程,或者协调各子进程间的活动。

(3) 负荷调节的需要。当实时系统中的工作负荷较重,已可能影响到对实时任务的控制时,可由系统把一些不重要的进程挂起,以保证系统能正常运行。

(4) 操作系统的需要。操作系统有时希望挂起某些进程,以便检查运行中的资源使用情况或进行记账。

linux下进程挂起的信号量——SIGHUP

 

 

进程队列

操作系统为每一类型进程建立一个或多个队列,队列元素为PCB。

状态的改变其实就是PCB入队出队的过程

就绪队列、等待队列可以是多个。单CPU的状态下,运行的进程只有一个,因此没有画出运行队列。

 

 

 

进程的实现

 

为了实现进程模型,操作系统维护着一张表格(一个结构数组),即进程表(process table)。每个进程占用一个进程表项。该表象包含进程状态的重要信息,包括程序计数器、堆栈指针、内存分配状况等,从而保证该进程随后能再次启动。

 

 

进程控制块——系统感知进程存在的唯一标志

又称进程描述符、进程属性

为了描述和控制进程的运行,系统为每个进程定义了一个数据结构——进程控制块PCB(Process Control Block)

PCB 中记录了操作系统所需的、用于描述进程的当前情况以及控制进程运行的全部信息。进程控制块的作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位,一个能与其它进程并发执行的进程。

Linux 系统中用task_struct 数据结构来描述每个进程的进程控制块

为了标识进程之间的家族关系,在 PCB 中都设置了家族关系表项,以标明自己的父进程及所有的子进程。

 

进程控制块中的信息

在进程控制块中,主要包括下述四方面的信息。

         

 

1) 进程标识符——进程标识符用于惟一地标识一个进程。一个进程通常有两种标识符:

(1) 内部标识符。在所有的操作系统中,都为每一个进程赋予了一个惟一的数字标识符,它通常是一个进程的序号。设置内部标识符主要是为了方便系统使用。

(2) 外部标识符。它由创建者提供,通常是由字母、数字组成,往往是由用户(进程)在访问该进程时使用。为了描述进程的家族关系,还应设置父进程标识及子进程标识。此外,还可设置用户标识,以指示拥有该进程的用户。



2) 处理机状态

处理机状态信息主要是由处理机的各种寄存器中的内容组成的。处理机在运行时,许多信息都放在寄存器中。当处理机被中断时,所有这些信息都必须保存在 PCB 中,以便在该进程重新执行时,能从断点继续执行


3) 进程调度信息

在 PCB 中还存放一些与进程调度和进程对换有关的信息,包括:

① 进程状态,指明进程的当前状态,作为进程调度和对换时的依据;

② 进程优先级,用于描述进程使用处理机的优先级别的一个整数,优先级高的进程应优先获得处理机;

③ 进程调度所需的其它信息,它们与所采用的进程调度算法有关,比如,进程已等待 CPU 的时间总和、进程已执行的时间总和等;

④ 事件,指进程由执行状态转变为阻塞状态所等待发生的事件,即阻塞原因。

 

4) 进程控制信息

进程控制信息包括:

① 程序和数据的地址,指进程的程序和数据所在的内存或外存地(首)址,以便再调度到该进程执行时,能从 PCB 中找到其程序和数据;

② 进程同步和通信机制,指实现进程同步和进程通信时必需的机制,如消息队列指针、信号量等,它们可能全部或部分地放在 PCB 中;

③ 资源清单,即一张列出了除 CPU 以外的、进程所需的全部资源及已经分配到该进程的资源的清单;

④ 链接指针,它给出了本进程(PCB)所在队列中的下一个进程的 PCB 的首地址。

 

 

 

进程表——所以进程PCB的集合

进程表大小固定。可以通过进程表确定系统最多有多少进程可以并发执行。

p_exec——找到可执行文件在磁盘上对应的位置

p_as——进程地址空间如何记录在proc结构中?因为进程地址空间是放了很多内容,每一项内都放在一段中。我们通过段来描述进程地址空间。将段按照地址大小的顺序,建立成一个AVL树,便于查找

 

 

 

 

多道程序设计模型

 

采用多道程序设计模型可以提高CPU的利用率。如何计算CPU的利用率?这里用到了概率论的知识。

 

假设一个进程等待I/O操作的时间与其停留在内存中的时间的比为p。当程序中同时又n个进程时。则所有n个进程都在等待I/O的概率为p的n次幂。因此,CPU的利用率为:

           

            CPU利用率=1-p的n次幂

 

n称为多道程序设计的道数

 

 


进 程 控 制

 

进程控制一般是由 OS的内核中原语来实现的。

原语(Primitive)是由若干条指令组成的,用于完成一定功能的一个过程。

它与一般过程的区别在于:它们是“原子操作(Action Operation)”。所谓原子操作,是指一个操作中的所有动作要么全做,要么全不做。换言之,它是一个不可分割的基本单位,因此,在执行过程中不允许被中断。原子操作在管态下执行,常驻内存。原语的作用是为了实现进程的通信和控制,系统对进程的控制如果不使用原语,就会造成其状态的不确定性,从而达不到进程控制的目的。

 

进程图

子进程可以继承父进程所拥有的资源,例如,继承父进程打开的文件,继承父进程所分配到的缓冲区等。当子进程被撤消时,应将其从父进程那里获得的资源归还给父进程。此外,在撤消父进程时,也必须同时撤消其所有的子进程。为了标识进程之间的家族关系,在 PCB 中都设置了家族关系表项,以标明自己的父进程及所有的子进程

 

父子进程共享的资源

  • 文件描述符
  • 共享缓冲区

 

 

引起创建进程的事件

在多道程序环境中,只有(作为)进程(时)才能在系统中运行。因此,为使程序能运行,就必须为它创建进程。导致一个进程去创建另一个进程的典型事件,可有以下四类:

(1) 用户登录。在分时系统中,用户在终端键入登录命令后,如果是合法用户,系统将为该终端建立一个进程,并把它插入就绪队列中。

(2) 作业调度。在批处理系统中,当作业调度程序按一定的算法调度到某作业时,便将该作业装入内存,为它分配必要的资源,并立即为它创建进程,再插入就绪队列中。

(3) 提供服务。当运行中的用户程序提出某种请求后,系统将专门创建一个进程来提供用户所需要的服务,例如,用户程序要求进行文件打印,操作系统将为它创建一个打印进程,这样,不仅可使打印进程与该用户进程并发执行,而且还便于计算出为完成打印任务所花费的时间。

(4) 应用请求。在上述三种情况下,都是由系统内核为它创建一个新进程;而第 4 类事件则是基于应用进程的需求,由它自己创建一个新进程,以便使新进程以并发运行方式完成特定任务。例如,某应用程序需要不断地从键盘终端输入数据,继而又要对输入数据进行相应的处理,然后,再将处理结果以表格形式在屏幕上显示。该应用进程为使这几个操作能并发执行,以加速任务的完成,可以分别建立键盘输入进程、表格输出进程。

 

 

进程的创建过程

一旦操作系统发现了要求创建新进程的事件后,便调用进程创建原语 Creat( )按下述步骤创建一个新进程。

(1) 申请空白 PCB。为新进程申请获得惟一的数字标识符,并从 PCB 集合中索取一个空白 PCB。

(2) 为新进程分配资源。为新进程的程序和数据以及用户栈分配必要的内存空间。

(3) 初始化进程控制块。PCB 的初始化包括:

  • ① 初始化标识信息,将系统分配的标识符和父进程标识符填入新 PCB 中;
  • ② 初始化处理机状态信息,使程序计数器指向程序的入口地址,使栈指针指向栈顶;
  • ③ 初始化处理机控制信息,将进程的状态设置为就绪状态或静止就绪状态,对于优先级,通常是将它设置为最低优先级,除非用户以显式方式提出高优先级要求。

(4) 将新进程插入就绪队列,如果进程就绪队列能够接纳新进程,便将新进程插入就绪队列。

 

 

进程的终止

如果系统中发生了上述要求终止进程的某事件,OS 便调用进程终止原语,按下述过程去终止指定的进程。

(1) 根据被终止进程的标识符,从 PCB 集合中检索出该进程的 PCB,从中读出该进程的状态

(2) 若被终止进程正处于执行状态,应立即终止该进程的执行,并置调度标志为真,用于指示该进程被终止后应重新进行调度。

(3) 若该进程还有子孙进程,还应将其所有子孙进程予以终止,以防它们成为不可控的进程。

(4) 将被终止进程所拥有的全部资源,或者归还给其父进程,或者归还给系统。

(5) 将被终止进程(PCB)从所在队列(或链表)中移出,等待其他程序来搜集信息。

 

 

Unix的几个进程控制操作

 

  • fork()——通过复制调用进程来建立新的进程。
  • exec()——通过一段新的程序覆盖原来的地址空间。通过覆盖,实现进程执行代码的转换
  • wait()——提供初级进程同步操作,能使一个进程等待另外一个进程的结束
  • exit()——用来终止一个进程的运行

 

Unix的fork()实现

 

  1. 为子进程分配一个空闲的进程描述符——PCB
  2. 分配给子进程唯一的标志pid
  3. 一次一页的方式复制父进程的地址空间
  4. 从父进程处继承共享资源,如打开的文件、当前的工作目录
  5. 将子进程的状态设置为就绪,插入到就绪队列
  6. 对子进程返回标识符0
  7. 向父进程返回子进程的pid

 

 

linux对于Unix的fork中第三步——一次一页的方式将父进程的地址空间复制给子进程——进行了改进

改进的方法——写时复制技术

因为父进程将所有的内容拷贝给子进程,而子进程往往所做的工作与父进程不同,因此子进程后续要用exec函数将拷贝过来的地址空间覆盖掉,因此复制工作就是无用功了。

linux使用写时复制技术,即父进程将指向地址空间的指针传递给子进程,并将权限设置为只读。当子进程需要写东西的时候,操作系统会为子进程单独开辟一块空间,将子进程的东西写入。

 

 


 

关于进程的讨论

 

 

进程地址空间

 

两个进程是两个地址空间。地址空间是隔离的。对于程序

开启两个进程运行该程序。分别输入参数myval 7 和 myval 8.运行结果如下:

打印出的地址空间相同。这说明两个进程共享了地址空间了吗?答案是否定的!先看一下进行的地址空间结构,

Myval中显示的相同的地址,指的是不同的进程地址空间中相同的位置,而不是相同的物理位置

 

 

进程映像——进程的快照

 

 


进 程 同 步

生产者-消费者(producer-consumer)问题是一个著名的进程同步问题。

它描述的是:

有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有 n 个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中;消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程和消费者进程都是以异步方式运行的,但它们之间必须保持同步,即不允许消费者进程到一个空缓冲区去取产品,也不允许生产者进程向一个已装满产品且尚未被取走的缓冲区中投放产品。

 

临界区

人们把在每个进程中访问临界资源的那段代码称为临界区(critical section)。

显然,若能保证诸进程互斥地进入自己的临界区,便可实现诸进程对临界资源的互斥访问。为此,每个进程在进入临界区之前,应先对欲访问的临界资源进行检查,看它是否正被访问。如果此刻该临界资源未被访问,进程便可进入临界区对该资源进行访问,并设置它正被访问的标志;如果此刻该临界资源正被某进程访问,则本进程不能进入临界区。因此,必须在临界区前面增加一段用于进行上述检查的代码,把这段代码称为进入区(entry section)。相应地,在临界区后面也要加上一段称为退出区(exit section)的代码,用于将临界区正被访问的标志恢复为未被访问的标志。

 

同步机制都应遵循下述四条准则:

(1) 空闲让进。当无进程处于临界区时,表明临界资源处于空闲状态,应允许一个请求进入临界区的进程立即进入自己的临界区,以有效地利用临界资源。
(2) 忙则等待。当已有进程进入临界区时,表明临界资源正在被访问,因而其它试图进入临界区的进程必须等待,以保证对临界资源的互斥访问。
(3) 有限等待。对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区,以免陷入“死等”状态。
(4) 让权等待。当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入“忙等”状态。

 

 

信号量机制 PV操作

 

 

1.整型信号量
最初由 Dijkstra 把整型信号量定义为一个用于表示资源数目的整型量 S,它与一般整型量不同,除初始化外,仅能通过两个标准的原子操作(Atomic Operation) wait(S)和 signal(S)来访问。很长时间以来,这两个操作一直被分别称为 P、V 操作

wait(S)和 signal(S)是两个原子操作,因此,它们在执行时是不可中断的。亦即,当一个进程在修改某信号量时,没有其他进程可同时对该信号量进行修改。此外,在 wait 操作中,对 S 值的测试和做 S:=S-1 操作时都不可中断

 

2.记录型信号量
在整型信号量机制中的 wait 操作,只要是信号量 S≤0,就会不断地测试。因此,该机制并未遵循“让权等待”的准则,而是使进程处于“忙等”的状态。

记录型信号量机制则是一种不存在“忙等”现象的进程同步机制。但在采取了“让权等待”的策略后,又会出现多个进程等待访问同一临界资源的情况。为此,在信号量机制中,除了需要一个用于代表资源数目的整型变量 value 外,还应增加一个进程链表指针 L,用于链接上述的所有等待进程。

在记录型信号量机制中,S.value 的初值表示系统中某类资源的数目,因而又称为资源信号量。对它的每次 wait 操作,意味着进程请求一个单位的该类资源,使系统中可供分配的该类资源数减少一个,因此描述为 S.value:=S.value-1;当 S.value<0 时,表示该类资源已分配完毕,因此进程应调用 block 原语,进行自我阻塞,放弃处理机,并插入到信号量链表S.L 中。可见,该机制遵循了“让权等待”准则。

对信号量的每次 signal 操作,表示执行进程释放一个单位资源,使系统中可供分配的该类资源数增加一个,故S.value:=S.value+1 操作表示资源数目加 1。若加 1 后仍是 S.value≤0,则表示在该信号量链表中,仍有等待该资源的进程被阻塞,故还应调用 wakeup 原语,将 S.L 链表中的第一个等待进程唤醒。如果 S.value 的初值为 1,表示只允许一个进程访问临界资源,此时的信号量转化为互斥信号量,用于进程互斥。

 

3.AND 型信号量
上述的进程互斥问题,是针对各进程之间只共享一个临界资源而言的。在有些应用场合,是一个进程需要先获得两个或更多的共享资源后方能执行其任务。

AND 同步机制的基本思想是:将进程在整个运行过程中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其它所有可能为之分配的资源也不分配给它。亦即,对若干个临界资源的分配,采取原子操作方式:要么把它所请求的资源全部分配到进程,要么一个也不分配。由死锁理论可知,这样就可避免上述死锁情况的发生。

 

 

 

管程机制——实现进程同步

 

虽然信号量机制是一种既方便、又有效的进程同步机制,但每个要访问临界资源的进程都必须自备同步操作 wait(S)和 signal(S)。这就使大量的同步操作分散在各个进程中。这不仅给系统的管理带来了麻烦,而且还会因同步操作的使用不当而导致系统死锁。这样,在解决上述问题的过程中,便产生了一种新的进程同步工具——管程(Monitors)。

 

1.管程的定义

系统中的各种硬件资源和软件资源,均可用数据结构抽象地描述其资源特性,即用少量信息和对该资源所执行的操作来表征该资源,而忽略了它们的内部结构和实现细节。

利用共享数据结构抽象地表示系统中的共享资源,而把对该共享数据结构实施的操作定义为一组过程,如资源的请求和释放过程 request 和 release。进程对共享资源的申请、释放和其它操作,都是通过这组过程对共享数据结构的操作来实现的,这组过程还可以根据资源的情况,或接受或阻塞进程的访问,确保每次仅有一个进程使用共享资源,这样就可以统一管理对共享资源的所有访问,实现进程互斥。代表共享资源的数据结构,以及由对该共享数据结构实施操作的一组过程所组成的资源管理程序,共同构成了一个操作系统的资源管理模块,我们称之为管程。管程被请求和释放资源的进程所调用。

 

2、管程由四部分组成:

  • ① 管程的名称;
  • ② 局部于管程内部的共享数据结构说明;
  • ③ 对该数据结构进行操作的一组过程;
  • ④ 对局部于管程内部的共享数据设置初始值的语句。

局部于管程内部的数据结构,仅能被局部于管程内部的过程所访问,任何管程外的过程都不能访问它;反之,局部于管程内部的过程也仅能访问管程内的数据结构。

由此可见,管程相当于围墙,它把共享变量和对它进行操作的若干过程围了起来,所有进程要访问临界资源时,都必须经过管程(相当于通过围墙的门)才能进入,而管程每次只准许一个进程进入管程,从而实现了进程互斥

 

 

条件变量


在利用管程实现进程同步时,必须设置同步工具,如两个同步操作原语 wait 和 signal。当某进程通过管程请求获得临界资源而未能满足时,管程便调用 wait 原语使该进程等待,并将其排在等待队列上。仅当另一进程访问完成并释放该资源之后,管程才又调用 signal 原语,唤醒等待队列中的队首进程。

但是仅仅有上述的同步工具是不够的。考虑一种情况:当一个进程调用了管程,在管程中时被阻塞或挂起,直到阻塞或挂起的原因解除,而在此期间,如果该进程不释放管程,则其它进程无法进入管程,被迫长时间地等待。为了解决这个问题,引入了条件变量condition。

通常,一个进程被阻塞或挂起的条件(原因)可有多个,因此在管程中设置了多个条件变量,对这些条件变量的访问,只能在管程中进行。管程中对每个条件变量都须予以说明,其形式为:Var x,y:condition。对条件变量的操作仅仅是 wait 和 signal,因此条件变量也是一种抽象数据类型,每个条件变量保存了一个链表,用于记录因该条件变量而阻塞的所有进程,同时提供的两个操作即可表示为 x.wait和 x.signal,其含义为:

① x.wait:正在调用管程的进程因 x 条件需要被阻塞或挂起,则调用 x.wait 将自己插入到 x 条件的等待队列上,并释放管程,直到 x 条件变化。此时其它进程可以使用该管程。

② x.signal:正在调用管程的进程发现 x 条件发生了变化,则调用 x.signal,重新启动一个因 x 条件而阻塞或挂起的进程。如果存在多个这样的进程,则选择其中的一个,如果没有,则继续执行原进程,而不产生任何结果。这与信号量机制中的 signal 操作不同,因为后者总是要执行 s:=s+1 操作,因而总会改变信号量的状态。

 

 

 

 

 

进程间通信

 

进程通信,是指进程之间的信息交换,其所交换的信息量少者是一个状态或数值,多者则是成千上万个字节。进程之间的互斥和同步,由于其所交换的信息量少而被归结为低级通信

在进程互斥中,进程通过只修改信号量来向其他进程表明临界资源是否可用。在生产者—消费者问题中,生产者通过缓冲池将所生产的产品传送给消费者。应当指出,信号量机制作为同步工具是卓有成效的,但作为通信工具,则不够理想,主要表现在下述两方面:

  • (1) 效率低,生产者每次只能向缓冲池投放一个产品(消息),消费者每次只能从缓冲区中取得一个消息;
  • (2) 通信对用户不透明。可见,用户要利用低级通信工具实现进程通信是非常不方便的。因为共享数据结构的

设置、数据的传送、进程的互斥与同步等,都必须由程序员去实现,操作系统只能提供共享存储器。
高级进程通信,是指用户可直接利用操作系统所提供的一组通信命令高效地传送大量数据的一种通信方式。操作系统隐藏了进程通信的实现细节。或者说,通信过程对用户是透明的。这样就大大减少了通信程序编制上的复杂性。

高级通信机制可归结为三大类:共享存储器系统、消息传递系统以及管道通信系统。

 

进程通信的类型

 

1.共享存储器系统

在共享存储器系统(Shared-Memory System)中,相互通信的进程共享某些数据结构或共享存储区,进程之间能够通过这些空间进行通信。据此,又可把它们分成以下两种类型:


(1) 基于共享数据结构的通信方式。在这种通信方式中,要求诸进程公用某些数据结构,借以实现诸进程间的信息交换。如在生产者—消费者问题中,就是用有界缓冲区这种数据结构来实现通信的。这里,公用数据结构的设置及对进程间同步的处理,都是程序员的职责。这无疑增加了程序员的负担,而操作系统却只须提供共享存储器。因此,这种通信方式是低效的,只适于传递相对少量的数据。

(2) 基于共享存储区的通信方式。为了传输大量数据,在存储器中划出了一块共享存储区,诸进程可通过对共享存储区中数据的读或写来实现通信。这种通信方式属于高级通信。进程在通信前,先向系统申请获得共享存储区中的一个分区,并指定该分区的关键字;若系统已经给其他进程分配了这样的分区,则将该分区的描述符返回给申请者,继之,由申请者把获得的共享存储分区连接到本进程上;此后,便可像读、写普通存储器一样地读、写该公用存储分区。

 

2.消息传递系统

消息传递系统(Message passing system)是当前应用最为广泛的一种进程间的通信机制。在该机制中,进程间的数据交换是以格式化的消息(message)为单位的;在计算机网络中,又把 message 称为报文。程序员直接利用操作系统提供的一组通信命令(原语),不仅能实现大量数据的传递,而且还隐藏了通信的实现细节,使通信过程对用户是透明的,从而大大减化了通信程序编制的复杂性,因而获得了广泛的应用。

特别值得一提的是,在当今最为流行的微内核操作系统中,微内核与服务器之间的通信,无一例外地都采用了消息传递机制。又由于它能很好地支持多处理机系统、分布式系统和计算机网络,因此它也成为这些领域最主要的通信工具。消息传递系统的通信方式属于高级通信方式。又因其实现方式的不同而进一步分成直接通信方式和间接通信方式两种。

 

3.管道通信


所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,又名 pipe 文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入管道;而接受管道输出的接收进程(即读进程),则从管道中接收(读)数据。由于发送进程和接收进程是利用管道进行通信的,故又称为管道通信。

为了协调双方的通信,管道机制必须提供以下三方面的协调能力:
(1) 互斥,即当一个进程正在对 pipe 执行读/写操作时,其它(另一)进程必须等待。
(2) 同步,指当写(输入)进程把一定数量(如 4 KB)的数据写入 pipe,便去睡眠等待,直到读(输出)进程取走数据后,再把它唤醒。当读进程读一空 pipe 时,也应睡眠等待,直至写进程将数据写入管道后,才将之唤醒。
(3) 确定对方是否存在,只有确定了对方已存在时,才能进行通信。

 

 

 

消息传递通信的实现方法

 

在进程之间通信时,源进程可以直接或间接地将消息传送给目标进程,由此可将进程通信分为直接通信和间接通信两种通信方式。

 

1.直接通信方式


这是指发送进程利用 OS 所提供的发送命令,直接把消息发送给目标进程。此时,要求发送进程和接收进程都以显式方式提供对方的标识符。通常,系统提供下述两条通信命令(原语)

Send(Receiver,message); 发送一个消息给接收进程;
Receive(Sender,message); 接收 Sender 发来的消息;

 

2.间接通信方式

 

间接通信方式指进程之间的通信需要通过作为共享数据结构的实体。该实体用来暂存发送进程发送给目标进程的消息;接收进程则从该实体中取出对方发送给自己的消息。通常把这种中间实体称为信箱。消息在信箱中可以安全地保存,只允许核准的目标用户随时读取。因此,利用信箱通信方式,既可实现实时通信,又可实现非实时通信。系统为信箱通信提供了若干条原语,分别用于信箱的创建、撤消和消息的发送、接收等。

(1) 信箱的创建和撤消。进程可利用信箱创建原语来建立一个新信箱。创建者进程应给出信箱名字、信箱属性(公用、私用或共享);对于共享信箱,还应给出共享者的名字。当进程不再需要读信箱时,可用信箱撤消原语将之撤消。

(2) 消息的发送和接收。当进程之间要利用信箱进行通信时,必须使用共享信箱,并利用系统提供的下述通信原语进行通信:
 

Send(mailbox,message); 将一个消息发送到指定信箱;
Receive(mailbox,message); 从指定信箱中接收一个消息;

 

 

消息缓冲队列通信机制

 

在这种通信机制中,发送进程利用 Send 原语将消息直接发送给接收进程;接收进程则利用 Receive 原语接收消息。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值