Skip to content

2.6 进程通信

目录


参考资料:

信号量机制也算是一种进程通信的方法,但是不能传递数据,并且效率低并且对用户不透明。

OS 提供的高级通信工具:

优点:

  1. 使用方便
  2. 高效地传送信息

四大类型:

  1. 共享存储器系统
  2. 管道通信系统
  3. 消息传递系统
  4. 客户机-服务机系统

共享存储器系统 sms

一个进程不能访问其他进程的地址空间,通过共享存储器系统可以创建一个共享的地址空间,所有的进程都可以访问这个地址空间。

需要自己控制互斥访问。

  1. 基于共享数据结构的通信方式:传输少量数据,属于低级通信
  2. 基于共享存储区的通信方式:传输大量数据,属于高级通信

管道通信系统 pipe PP

管道通信系统的本质是在内存中开辟一块缓冲区,缓冲区与管道相关联,进程对管道的操作实际上就是对缓冲区的操作。

管道通信系统的特点:

  • 半双工的通信方式(可以创建 2 个管道实现全双工)

匿名管道

shell
# EG Linux
command1 | command2

ps -ef | grep nginx

指令 | 的本质就是创建了一个匿名管道,将前一个指令的输出作为后一个指令的输入。

匿名管道的特点:

  • 只能在具有公共祖先的进程之间使用。
  • 通过管道符 | 创建的管道是匿名管道,用完了就会被自动销毁
C
// Linux 底层使用pipe()函数来创建匿名管道
// 创建成功返回0,失败返回-1
// fd[0] 是管道的读端,fd[1] 是管道的写端
int pipe (int fd[2]);

命名管道/有名管道 FIFO

命名管道的特点:

  • 可以在不具有公共祖先的进程之间使用,通过访问文件的方式来使用
  • 用完了不会自动销毁,需要手动销毁
  • 有名管道是一种特殊的文件,可以通过 mkfifo 命令来创建

消息传递系统 MPS

直接消息传递

OS 提供了发送和接受消息的原语 send receive 来实现直接消息传递。

原语

  1. 对称寻址方式

需要知道对方的进程标识符

c
send(receiver, message); // 发送消息给一个进程
receive(sender, message); // 接收一个进程的消息
  1. 非对称寻址方式
c
send(receiver, message); // 发送消息给一个进程
receiver(id, message); // 接收一个进程的消息

消息格式

本地的话消息格式简单一点

进程间同步方式

通信链路

  1. 单向链路:
  2. 双向链路:

消息缓冲队列通信机制

消息缓冲队列通信机制是一种间接消息传递机制。

需要发送的进程将消息放入消息缓冲队列中,接收的进程从消息缓冲队列中取出消息。

这个消息缓存记录在 PCB 中,每个 PCB 都有一个消息缓冲队列。

imgimg 数据结构:

ts
/**
 * 消息缓冲区
 */
type struct message_buffer {
    int sender;                         //发送者进程标识符
    int size;                           //消息长度
    char text;                          //消息正文
    struct message buffer next;         //指向下一个消息缓冲区的指针
}

/**
 * PCB 进程控制块
 */
type struct processcontrol_block {
    ...
    struct message_buffer mq;        //消息队列队首指针
    semaphore mutex;                 //消息队列互斥信号量
    semaphore sm;                    //消息队列资源信号量
    ...
} PCB;

发送原语

c
void send(receiver,a) {        //1-发送区:receiver为接收进程标识符,a为发送区首址;

    getbuf(a.size,i);           //2-缓冲区:根据a.size申请缓冲区;

    copy(i.sender,a.sender);   //3-缓冲区复制操作:将发送区a中的信息复制到消息缓冲区i中;
    i.size=a.size;
    copy(i.text,a.text);
    i.next=0;

    getid(PCBset,receiver.j);  //4-获取接收者标识:获得接收进程内部的标识符;

    P(j.mutex);
    insert(&j.mq,i);           //5-消息插入:将消息缓冲区插入接受进程的消息队列;
    V(j.mutex);

    V(j.sm);               //6-唤醒数据消费:资源信号量+1
}

接收原语

c
void receive(b) {
    j = internal name;          //j为接收进程内部的标识符;

    P(j.sm);                 //如果资源为空,此时将阻塞

    P(j.mutex);              //使用同步原语操作j.mq
    remove(j.mq, i);            //将消息队列中第一个消息移出;
    V(j.mutex);

    copy(b.sender, i.sender);   //缓冲区->接收区,将消息缓冲区i中的信息复制到接收区b:
    b.size =i.size;
    copy(b.text, i.text);
    releasebuf(i);              //释放消息缓冲区;
}

间接消息传递

发送和接受消息,都通过共享中间实体(信箱)来实现。

信箱通信

一个信箱包括一个信箱头和一个信箱体,信箱体包含多个信箱单元,每个信箱单元都可以存放一个消息。

信箱存放在内存中。

  • 信箱头:记录信箱的状态
    • 信箱的标识符
    • 信箱的拥有者
    • 信箱口令
    • 信箱空格数
  • 信箱体:有多个各自存放消息

操作系统提供创建,销毁,发送和接收等原语来实现信箱通信

ts
// 发送消息
send(mailbox, message);
// 接收消息
receive(mailbox, message);

信箱类型:

  1. 私有信箱:只能被一个进程拥有,其他进程只允许往这个信箱里发送消息,进程结束时消失
  2. 公共邮箱:所有进程都可以读取或者发送消息到邮箱,邮箱一般在系统运行期间一直存在
  3. 共享邮箱:创建时需要指定共享的对象,拥有者和共享者都有权收取自己的消息

进程间的关系:

  1. 一对一
  2. 多对一:一个服务多个客户
  3. 多对多:多个服务多个客户
  4. 一对多:一个发送进程多个接收进程,类似广播

客户机-服务机系统

可以在不同的计算机上运行的进程之间进行通信。

socket 套接字

一个套接字包含:

  • 通信目的地地址
  • 通信使用的端口号
  • 通信网络的传输层协议
  • 进程所在的网络地址
  • API
  1. 基于文件型

类似管道,套接字关联到一个文件,进程通过对文件的操作来实现通信。

  1. 基于网络型

分为服务端和客户端,服务端绑定一个端口,客户端通过端口来访问服务端。

客户端的端口号是动态分配的。

远程过程调用 RPC & 远程方法调用

远程调用允许一个进程调用另一个在不同地址空间的进程的过程,就像调用本地过程一样。

Copyright © 2022 田园幻想乡 浙ICP备2021038778号-1