IPC与RPC通信实现方式

IPC与RPC通信实现方式

一、IPC(进程间通信)通信实现方式

1. 管道(Pipe)
1.1 核心原理

管道是内核维护的单向字节流缓存区,数据按"先进先出(FIFO)"规则传输,本质是内核中的一块临时内存区域,通信双方通过文件描述符对该缓存区进行读写操作,实现进程间数据传递。

1.2 分类与实现细节
维度 匿名管道(Unnamed Pipe) 命名管道(FIFO)
创建接口 Linux:pipe(int fd[2]) 系统调用;Windows:CreatePipe() 函数 Linux:mkfifo(const char *pathname, mode_t mode) 命令/函数;Windows:CreateNamedPipe() 函数
血缘关系依赖 仅支持父子/兄弟进程 (通过 fork 继承文件描述符) 支持任意无血缘关系进程(通过文件系统路径唯一标识)
持久化特性 随进程退出自动销毁,无文件系统实体 存在文件系统节点(如 /tmp/myfifo),文件节点持久化,数据读取后释放
通信方向 半双工(同一时刻只能单向传输,需创建2个管道实现双向通信) 半双工(可通过配置实现全双工模式)
读写特性 读端阻塞等待写端输入,写端满时阻塞 无数据时读端阻塞,无读端时写端报错(ENXIO
适用场景 父进程与子进程的简单数据交互(如 `ls grep` 命令管道)
1.3 实现步骤(Linux 匿名管道)
  1. 创建管道 :调用 pipe(fd),生成两个文件描述符 fd[0](读端)、fd[1](写端)。
  2. 创建子进程 :调用 fork(),子进程继承父进程的管道文件描述符。
  3. 关闭无用端 :父进程关闭读端 fd[0],子进程关闭写端 fd[1],形成单向通信通道。
  4. 数据传输 :父进程通过 fd[1] 写入数据,子进程通过 fd[0] 读取数据。
  5. 释放资源 :通信结束后关闭剩余文件描述符,子进程调用 exit(),父进程调用 wait() 回收子进程。
1.4 代码示例与执行结果
c 复制代码
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    int fd[2];
    // 1. 创建管道
    if (pipe(fd) == -1) {
        perror("pipe failed");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // 子进程:关闭写端,读取数据
        close(fd[1]);
        char buf[1024];
        ssize_t bytes_read = read(fd[0], buf, sizeof(buf)-1);
        if (bytes_read > 0) {
            buf[bytes_read] = '\0';
            printf("子进程读取到数据:%s\n", buf);
        }
        close(fd[0]);
        exit(EXIT_SUCCESS);
    } else {
        // 父进程:关闭读端,写入数据
        close(fd[0]);
        const char *msg = "Hello from Parent Process via Anonymous Pipe!";
        write(fd[1], msg, strlen(msg));
        close(fd[1]);
        wait(NULL); // 等待子进程结束
        printf("父进程完成数据发送\n");
    }
    return 0;
}

编译执行

bash 复制代码
gcc pipe_demo.c -o pipe_demo
./pipe_demo

预期结果

复制代码
子进程读取到数据:Hello from Parent Process via Anonymous Pipe!
父进程完成数据发送
2. 消息队列(Message Queue)
2.1 核心原理

消息队列是内核维护的结构化消息链表,数据以"消息类型+消息正文"的格式存储,进程可按消息类型选择性读取数据,无需遵循FIFO顺序,支持异步通信。消息队列独立于进程存在,进程退出后消息不会丢失,直到被读取或手动删除。

2.2 核心接口(Linux)
接口函数 功能描述
msgget(key_t key, int msgflg) 创建或获取消息队列,返回队列ID;key 为队列标识,msgflg 为权限标志(如 IPC_CREAT
msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) 向队列发送消息;msgp 为消息结构体指针,msgsz 为消息正文长度
msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) 从队列读取消息;msgtyp 指定消息类型(0=任意类型,>0=指定类型,<0=最小类型)
msgctl(int msqid, int cmd, struct msqid_ds *buf) 控制消息队列;cmd 常用 IPC_RMID(删除队列)
2.3 消息结构体规范
c 复制代码
struct msgbuf {
    long mtype;     // 消息类型(必须 > 0)
    char mtext[1024];// 消息正文
};
2.4 适用场景

适用于进程间异步、非实时的数据传输,如日志收集进程接收多个业务进程的日志消息。

3. 共享内存(Shared Memory)
3.1 核心原理

共享内存是内核分配的一块物理内存区域 ,映射到多个进程的虚拟地址空间,进程可直接读写该内存区域,无需内核态与用户态的数据拷贝,是性能最高的IPC方式

由于多个进程可同时访问共享内存,需配合信号量实现同步与互斥(避免读写冲突,如进程A写入时进程B禁止读取)。

3.2 核心接口(Linux)
接口函数 功能描述
shmget(key_t key, size_t size, int shmflg) 创建或获取共享内存段,返回内存ID;size 为内存大小,shmflg 为权限标志
shmat(int shmid, const void *shmaddr, int shmflg) 将共享内存映射到进程虚拟地址空间,返回映射后的地址;shmaddr 为指定映射地址(NULL 由系统分配)
shmdt(const void *shmaddr) 解除进程与共享内存的映射关系,不删除内存段
shmctl(int shmid, int cmd, struct shmid_ds *buf) 控制共享内存;cmd 常用 IPC_RMID(删除内存段)
3.3 实现步骤
  1. 进程A调用 shmget 创建共享内存段。
  2. 进程A调用 shmat 将内存映射到自身虚拟地址,写入数据。
  3. 进程B调用 shmget 获取同一内存段,调用 shmat 映射到自身地址。
  4. 进程B读取内存数据(需信号量同步)。
  5. 通信结束后,进程A、B调用 shmdt 解除映射,进程A调用 shmctl 删除内存段。
3.4 适用场景

适用于高频、大数据量的进程间通信,如图像处理进程与数据采集进程的实时数据交换。

4. 信号量(Semaphore)
4.1 核心原理

信号量是内核维护的计数器 ,用于实现进程间的同步与互斥,不直接传输数据 。核心操作是 P操作(申请资源,计数器-1,无资源则阻塞)和 V操作(释放资源,计数器+1,唤醒阻塞进程)。

  • 互斥:信号量初始值为1,进程进入临界区前执行P操作,退出时执行V操作,确保同一时刻只有一个进程访问临界资源。
  • 同步:信号量初始值为0,进程A完成任务后执行V操作,进程B执行P操作阻塞等待,直到进程A完成。
4.2 核心接口(Linux)
接口函数 功能描述
semget(key_t key, int nsems, int semflg) 创建或获取信号量集,返回信号量集ID;nsems 为信号量数量
semop(int semid, struct sembuf *sops, unsigned nsops) 执行P/V操作;sops 为操作结构体数组,nsops 为操作数量
semctl(int semid, int semnum, int cmd, ...) 控制信号量;cmd 常用 SETVAL(设置初始值)、IPC_RMID(删除信号量集)
5. 信号(Signal)
5.1 核心原理

信号是内核向进程发送的异步通知,用于通知进程发生了某个事件(如中断、异常、用户指令)。信号仅能传递一个整数标识(信号编号),无法传输大量数据,是粒度最粗的IPC方式。

5.2 常见信号(Linux)
信号编号 信号名 含义 默认处理方式
2 SIGINT 中断信号(Ctrl+C) 终止进程
9 SIGKILL 强制终止信号(无法捕获/忽略) 终止进程
15 SIGTERM 终止信号(kill 命令默认发送) 终止进程
17 SIGCHLD 子进程状态变化信号(退出/暂停) 忽略
5.3 核心接口(Linux)
接口函数 功能描述
signal(int signum, void (*handler)(int)) 设置信号处理函数;handler 为自定义处理函数或 SIG_IGN(忽略)、SIG_DFL(默认)
kill(pid_t pid, int sig) 向指定进程发送信号;pid 为进程ID,sig 为信号编号
5.4 适用场景

适用于简单事件通知 ,如父进程通过 SIGCHLD 捕获子进程退出事件,或用户通过 SIGINT 终止前台进程。

6. UNIX域套接字(Unix Domain Socket, UDS)
6.1 核心原理

UNIX域套接字是基于文件系统的套接字机制,仅用于本机进程间通信,接口与网络套接字(TCP/UDP)完全一致,但无需经过网络协议栈(无IP、端口解析开销),性能优于TCP套接字。

支持两种通信模式:

  • 字节流模式(SOCK_STREAM):可靠、面向连接,类似TCP。
  • 数据报模式(SOCK_DGRAM):无连接、不可靠,类似UDP。
6.2 核心接口(Linux)

与网络套接字接口一致,仅地址结构不同:

c 复制代码
struct sockaddr_un {
    sa_family_t sun_family;  // 协议族,固定为 AF_UNIX
    char sun_path[108];      // 套接字文件路径,如 "/tmp/uds.sock"
};
6.3 实现步骤(字节流模式)
  1. 服务端调用 socket(AF_UNIX, SOCK_STREAM, 0) 创建套接字。
  2. 服务端绑定地址 sockaddr_un,调用 listen() 监听连接。
  3. 客户端调用 socket() 创建套接字,调用 connect() 连接服务端。
  4. 服务端调用 accept() 接受连接,双方通过 read()/write() 传输数据。
  5. 通信结束后,双方调用 close() 关闭套接字,删除套接字文件。
6.4 适用场景

适用于本机跨进程的可靠通信,如数据库服务(MySQL)与本机客户端的通信。

二、RPC(远程过程调用)通信实现方式

1. RPC 核心定义与架构

RPC(Remote Procedure Call)是跨主机的远程函数调用机制,核心目标是让调用方像调用本地函数一样调用远程服务器的函数,屏蔽网络通信、数据序列化等底层细节。

1.1 核心架构组件
组件名称 功能描述
客户端(Client) 远程函数调用的发起方,调用本地存根函数
客户端存根(Client Stub) 封装网络通信细节:将函数参数序列化 为字节流,发送给服务端;接收服务端响应,反序列化为返回值
服务端存根(Server Stub) 接收客户端请求:将字节流反序列化 为函数参数,调用服务端本地函数;将函数返回值序列化,发送给客户端
服务端(Server) 提供远程函数的具体实现逻辑
网络传输层 负责客户端与服务端的数据传输,基于TCP/UDP/HTTP2等协议
1.2 核心流程(同步RPC)
  1. 客户端调用本地存根函数(如 say_hello("Alice"))。
  2. 客户端存根将函数名、参数序列化为二进制字节流。
  3. 客户端存根通过网络传输层将字节流发送到服务端。
  4. 服务端存根接收字节流,反序列化为函数名和参数。
  5. 服务端存根调用服务端本地函数(如 local_say_hello("Alice"))。
  6. 服务端函数执行完成,返回结果给服务端存根。
  7. 服务端存根将结果序列化,通过网络发送给客户端。
  8. 客户端存根接收结果,反序列化为本地数据,返回给客户端。
2. RPC 关键技术环节
2.1 序列化/反序列化

序列化是将内存中的数据结构(如结构体、对象)转换为可传输的字节流的过程;反序列化是其逆过程,是RPC的基础技术。

序列化协议 特点 适用场景
JSON 文本格式,易读易调试,性能较低 轻量级跨语言通信、接口调试
XML 文本格式,扩展性强,性能低 传统企业级系统
Protobuf 二进制格式,高性能、高压缩率,需定义IDL 高性能跨语言RPC(如gRPC)
Thrift 二进制格式,支持多种传输协议,跨语言 大规模分布式系统(如Hadoop)
MsgPack 二进制格式,兼容JSON语法,性能高 移动端与服务端通信

IDL(接口定义语言) :用于定义远程函数的接口规范,如Protobuf的 .proto 文件、Thrift的 .thrift 文件,通过编译器生成多语言的存根代码。

2.2 网络传输协议

RPC底层依赖网络传输协议,不同协议的性能和适用场景不同:

传输协议 特点 典型框架
TCP 可靠字节流,面向连接,性能稳定 Dubbo、gRPC
UDP 无连接,低延迟,不可靠 实时音视频通信、游戏服务
HTTP2 多路复用、二进制帧、头部压缩,兼容HTTP gRPC、Spring Cloud Gateway
自定义TCP协议 按需定制报文格式,性能最优,开发成本高 Dubbo默认协议
2.3 通信模型
通信模型 特点 适用场景
同步RPC 客户端调用后阻塞,直到收到服务端响应 大部分业务场景(如订单查询)
异步RPC 客户端调用后不阻塞,通过回调/Future获取结果 高并发场景(如秒杀系统)
单向RPC 客户端发送请求,无需等待响应 日志上报、数据采集
流式RPC 客户端/服务端批量发送数据流,支持双向流 大数据传输、实时监控
3. 主流RPC框架实现方式
3.1 基于TCP自定义协议的RPC框架------Dubbo

Dubbo是阿里巴巴开源的高性能Java RPC框架,基于TCP协议实现自定义报文格式,适用于微服务架构。

3.1.1 核心特性
  • 传输层:基于Netty(NIO框架)实现TCP通信,支持长连接。
  • 序列化:支持Protobuf、Hessian2、JSON等多种协议。
  • 服务治理:内置负载均衡、熔断降级、服务注册发现(基于ZooKeeper)。
3.1.2 实现步骤
  1. 定义接口(如 HelloService),使用Dubbo注解标记。
  2. 服务端实现接口,配置服务端口、序列化协议、注册中心地址。
  3. 服务端启动,将服务注册到ZooKeeper。
  4. 客户端引用接口,配置注册中心地址,通过代理对象调用远程方法。
  5. 底层流程:客户端代理(存根)序列化参数 → Netty发送TCP请求 → 服务端接收请求反序列化 → 调用实现类 → 序列化结果返回。
3.2 基于HTTP2的跨语言RPC框架------gRPC

gRPC是Google开源的跨语言RPC框架,基于Protobuf序列化、HTTP2传输,支持流式RPC。

3.2.1 核心特性
  • 跨语言:支持C++、Java、Python、Go等数十种语言。
  • 高性能:HTTP2多路复用,减少连接数;Protobuf二进制序列化。
  • 流式RPC:支持客户端流、服务端流、双向流。
3.2.2 实现步骤(以Go语言为例)
  1. 定义Protobuf IDL

    proto 复制代码
    syntax = "proto3";
    package helloworld;
    
    service Greeter {
        rpc SayHello (HelloRequest) returns (HelloReply);
    }
    
    message HelloRequest {
        string name = 1;
    }
    
    message HelloReply {
        string message = 1;
    }
  2. 生成存根代码

    bash 复制代码
    protoc --go_out=. --go-grpc_out=. helloworld.proto
  3. 服务端实现

    • 实现 GreeterServer 接口的 SayHello 方法。
    • 启动gRPC服务,监听指定端口。
  4. 客户端调用

    • 连接服务端,创建 GreeterClient 实例。
    • 调用 SayHello 方法,底层自动完成序列化、传输、反序列化。
3.3 基于RESTful的RPC框架------Spring Cloud OpenFeign

OpenFeign是Spring Cloud生态的声明式HTTP RPC框架,基于RESTful风格,本质是HTTP客户端的封装。

3.3.1 核心特性
  • 声明式接口:通过注解定义远程接口(如 @FeignClient)。
  • 序列化:默认使用JSON,兼容多种格式。
  • 集成服务治理:与Eureka、Nacos等注册中心无缝集成。
3.3.2 实现步骤
  1. 服务端提供RESTful接口(如 /api/hello)。

  2. 客户端定义Feign接口:

    java 复制代码
    @FeignClient(name = "hello-service")
    public interface HelloClient {
        @GetMapping("/api/hello")
        String sayHello(@RequestParam String name);
    }
  3. 客户端注入 HelloClient,直接调用方法,底层自动发送HTTP请求并解析响应。

4. 基于消息队列的异步RPC框架

异步RPC基于消息队列(如Kafka、RabbitMQ)实现,客户端将请求发送到消息队列,服务端消费队列并处理请求,处理完成后将结果发送到结果队列,客户端消费结果队列获取响应。

4.1 核心特性
  • 解耦:客户端与服务端无需直接通信,通过消息队列解耦。
  • 削峰填谷:应对突发流量,消息队列缓存请求。
  • 重试机制:请求处理失败时可重新入队重试。
4.2 适用场景

适用于非实时、高并发的业务场景,如订单创建、日志处理。

三、IPC与RPC核心区别对比表

对比维度 IPC(进程间通信) RPC(远程过程调用)
通信范围 同一台主机内的不同进程 跨主机的不同进程(支持跨网络)
核心目标 解决本机进程间的数据交换与同步 实现远程函数调用,屏蔽网络细节
底层依赖 内核机制(管道、共享内存)、本机套接字 网络协议(TCP/UDP/HTTP2)、序列化协议
性能开销 低(无网络传输、序列化开销) 高(需序列化、网络传输、反序列化)
地址标识 进程ID、文件路径、键值(key) IP地址+端口号、服务名(注册中心)
典型应用场景 本机多进程协作(如浏览器多进程、数据库) 分布式系统、微服务(如电商订单服务与支付服务)
跨平台/跨语言能力 弱(依赖操作系统内核机制) 强(如gRPC支持多语言)
相关推荐
智联视频超融合平台1 小时前
智能互联新时代:视频联网平台与物联网的完美融合
人工智能·物联网·网络协议·系统安全·音视频
爱吃番茄鼠骗1 小时前
龙芯久久派2k0300联网教程
网络
Ares-Wang1 小时前
网络》》VLAN、交换机 、AP、无线扩展器
网络
2501_915106321 小时前
Charles抓包怎么用 Charles抓包工具详细教程、网络调试方法、HTTPS配置与手机抓包实战
网络·ios·智能手机·小程序·https·uni-app·webview
xing.yu.CTF1 小时前
ATT&CK实战系列--蓝队防御(二)
网络·web安全·渗透测试·横向移动·暴力破解·入侵排查·内网对抗
Cher ~2 小时前
路由器 详解
网络·智能路由器
代码不行的搬运工2 小时前
针对BGP前缀劫持的实用防御(2007)
运维·网络
ReaF_star2 小时前
【安防】Windows Server 2008虚拟机忘记密码的一时兴起
网络·windows·安全
渡我白衣2 小时前
多路转接模型与select
人工智能·深度学习·websocket·网络协议·机器学习·网络安全·信息与通信