当前位置: 首页 > news >正文

进程间的通信

一.理解

1.进程间通信的目的

数据传输,资源共享,通知事件,进程控制

2.进程间通信的本质

先让不同的进程看到"同一份"资源(该资源只能由OS系统提供,不能由任何一个进程提供)

3.具体通信方式

<1>基于文件的管道通信

匿名管道通信,命名管道通信

注:一个管道只能用于单项通信

<2>System V本机通信

消息队列,信号量,共享内存

注1:System V是一种标准,Linux内核支持该标准,并专门设计了一个IPC通信模块

注2:IPC本质是让不同进程看到同一块资源

4.管道的容量

Ubuntu下管道的大小为64KB

5.管道的写入原子性

对管道进行写入只有写入失败和写入成功两种情况

6.有关管道的基本接口

<1>ssize_t read(int fd,void* buf,size_t count);

1)功能:从管道中读取数据

2)参数解释

  • fd:管道的文件描述符(读端)。
  • buf:用于存储读取数据的缓冲区。
  • count:要读取的最大字节数。

3)头文件:#include <unistd.h>

4)返回值

  • 成功时返回实际读取的字节数。
  • 失败时返回-1,并设置errno
  • 如果管道为空,read()会阻塞,直到有数据可读(除非设置了O_NONBLOCK标志)。

<2>ssize_t write(int fd,const void* buf,size_t count);

1)功能:向管道中写入数据

2)参数解释

  • fd:管道的文件描述符(写端)。
  • buf:包含要写入数据的缓冲区。
  • count:要写入的最大字节数。

3)头文件:#include <unistd.h>

4)返回值

  • 成功时返回实际写入的字节数。
  • 失败时返回-1,并设置errno
  • 如果管道已满,write()会阻塞,直到有空间可写(除非设置了O_NONBLOCK标志)。

<3>int close(int fd);

1)功能:关闭管道的文件描述符,释放资源。

2)参数解释

  • fd:要关闭的文件描述符。

3)头文件:#include <unistd.h>

4)返回值

  • 成功时返回0。
  • 失败时返回-1,并设置errno

二.匿名管道

1.是什么?

匿名管道(Anonymous Pipe)是一种未命名的管道,它只能在本地计算机中使用,并且不可用于网络间的通信。匿名管道通过内存中共享的一段缓冲区实现进程间通信,数据从管道的写端进入,从读端被读取,遵循先进先出(FIFO)的原则。

2.使用条件

用于有血缘关系的进程间的通信(多用于父子进程间通信)

3.5种特性和4种通信情况

5种特性

<1>匿名管道是能用于进行有血缘关系的进程间的通信,多用于父子

<2>管道文件自带同步机制

<3>管道是面向字节流的

<4>管道是单向通信的(属于半双工的一种特殊情况)

<5>(管道)文件的生命周期是随进程的

4种通信情况

<1>写慢,读快:读端进入阻塞状态等待写端写入

<2>写快,读慢:写满了的时候写端进入阻塞状态,等待读端

<3>写关,继续读:read()返回0,表示文件结尾

<4>读关,写继续:写端再写入没有意义,此时OS杀死写端进程,发送异常信号13 SIGPIPE

4.匿名管道的相关接口

int pipe(int pipefd[2]);

<1>功能:用于创建无名管道(匿名管道)

<2>参数解释

pipefd:一个包含两个整型元素的数组,用于返回管道的文件描述符。

pipefd[0]:管道的读端文件描述符。

pipefd[1]:管道的写端文件描述符。

<3>头文件:#include<unistd.h>

<4>返回值

成功时返回0,并将两个文件描述符存储在pipefd数组中。

失败时返回-1,并设置errno以指示错误原因。

<5>代码实现

#include <unistd.h>
#include <stdio.h>

int main() {
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return 1;
    }
    printf("读端文件描述符: %d\n", pipefd[0]);
    printf("写端文件描述符: %d\n", pipefd[1]);
    return 0;
}

5.基于匿名管道实现父子进程间通信

#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main() {
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return 1;
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) { // 子进程
        close(pipefd[0]); // 关闭读端
        const char *msg = "Hello from child!";
        write(pipefd[1], msg, strlen(msg));
        close(pipefd[1]); // 关闭写端
        _exit(0);
    } else { // 父进程
        close(pipefd[1]); // 关闭写端
        char buffer[256];
        ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer) - 1);
        if (bytes_read == -1) {
            perror("read");
            return 1;
        }
        buffer[bytes_read] = '\0';
        printf("Received from child: %s\n", buffer);
        close(pipefd[0]); // 关闭读端
    }

    return 0;
}

三.命名管道

1.是什么?

命名管道是一种特殊的文件类型,它在文件系统中有一个对应的路径名。不同进程可以通过这个路径名来访问同一个管道,从而实现进程间通信。

2.使用条件

任何两个进程间均可借助命名管道进行通信

3.命名管道的相关接口

int mkfifo(const char* pathname,mode_t mode);

<1>功能:用于创建有名管道(FIFO),有名管道在文件系统中有一个名字,可以用于无亲缘关系的进程间通信。

<2>参数解释

  • pathname:有名管道的路径名。
  • mode:文件的访问权限,通常使用S_IRUSR | S_IWUSR来设置读写权限。

<3>头文件:#include <sys/stat.h> 和 #include <sys/types.h>

<4>返回值

  • 成功时返回0。
  • 失败时返回-1,并设置errno

<5>代码实现

#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>

int main() {
    const char *fifo_name = "my_fifo";
    mode_t mode = 0666; // 设置读写权限
    if (mkfifo(fifo_name, mode) == -1) {
        perror("mkfifo");
        return 1;
    }
    printf("有名管道 '%s' 创建成功\n", fifo_name);
    return 0;
}

4.匿名管道 VS 命名管道

特性匿名管道命名管道(FIFO)
命名无名字有名字,存在于文件系统中
通信范围只能在具有亲缘关系的进程间通信可以在任意进程间通信
创建方式通过pipe()函数创建通过mkfifo()函数创建
打开方式直接返回文件描述符需要通过open()函数打开
阻塞行为读写操作可能阻塞读写操作可能阻塞
生命周期随进程结束而关闭除非显式删除,否则一直存在

四.共享内存

1.是什么?

共享内存是一种特殊的内存区域,可以被多个进程同时访问。这些进程可以将同一段共享内存连接到它们自己的地址空间中,从而直接读写这块内存区域。共享内存是进程间通信速度最快的方式之一,因为它避免了数据在内核和用户空间之间的复制。

2.基本原理

<1>创建共享内存段:使用shmget函数在内核中创建或打开一个共享内存段。该函数需要指定共享内存段的键(key)、大小(size)和权限(shmflg)等参数。

<2>映射共享内存段:使用shmat函数将共享内存段映射到当前进程的地址空间中。这样,进程就可以像访问普通内存一样访问共享内存段。

<3>读写共享内存段:进程可以直接对共享内存段进行读写操作。由于多个进程共享同一块内存区域,因此一个进程对共享内存段的修改会立即影响到其他进程。

<4>解除映射和删除共享内存段:当进程不再需要访问共享内存段时,可以使用shmdt函数解除映射。当所有进程都解除映射后,可以使用shmctl函数删除共享内存段。

注:共享内存资源的生命周期是随内核的,若是进程结束后没有删除共享内存,他将一直存在

3.使用共享内存的接口

<1>int shmget(key_t key,size_t size,int shmflag);

1)功能:创建共享内存

2)参数解释:

  • key:唯一标识符(通常用 ftok 生成),标识共享内存的唯一性。
  • size:共享内存段的大小(建议为 4KB 的整数倍)。
  • shmflg:标志位

shmflg的可能填写方法:

1.IPC_CREAT:创建共享内存,如果共享内存不存在就创建,存在就直接打开这个已经存在的共享内存并返回

2.IPC_EXCL :单独使用无意义,必须组合使用,可以兼容权限设置,以便后续对于共享内存数据的读操作:IPC_CREAT | IPC_EXCL | 0666 :不存在就创建,否则就出错返回

3)头文件:#include<sys/shm.h>

4)返回值:成功返回共享内存标识符(shmid),失败返回 -1

<2>int shmctl(int shmid,int cmd,struct shmid_ds *buf);

1)功能:控制共享内存段(如删除、获取属性)。

2)参数解释

  • cmd:操作命令(如 IPC_RMID 删除共享内存)。
  • buf:存储或设置属性的结构体指针。
  • shmid:共享内存标识符

3)头文件:#include<sys/shm.h>

4)返回值:成功返回 0,失败返回 -1

<3>void* shmat(int shmid,const void* shmaddr,int shmflag);

1)功能:将共享内存段映射到进程的地址空间。

2)参数解释

  • shmid:共享内存标识符。
  • shmaddr:映射地址(通常设为 NULL,由系统自动选择)。
  • shmflg:标志位(如 SHM_RDONLY 只读模式)。

3)头文件:#include<sys/shm.h>

4)返回值:成功返回映射后的地址(起始虚拟地址),失败返回 (void *)-1

<4>int shmdt(const void* shmaddr);

1)功能:将共享内存段与进程地址空间分离。

2)参数解释:shmaddr 为 shmat 返回的地址。

3)头文件:#include<sys/shm.h>

4)返回值:成功返回 0,失败返回 -1

<5>key_t ftok(const char* pathname,int proj_id);

1)功能:生成唯一标识符 key

2)参数解释

  • pathname:存在的文件路径。
  • proj_id:项目标识符(非零字符)。

3)头文件:#include<sys/ipc.h>

4)返回值:成功返回 key,失败返回 -1

4.查看/删除共享内存的命令

<1>ipcs -m:查看创建的共享内存

<2>ipcrm -m shmid:删除创建的标识符为shmid的共享内存

5.共享内存的代码实现

#pragma once
#include<iostream>
#include<cstdio>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<string>

const int gdefaultid=-1;
const int gsize=4096;
const std: :string pathname= ".";
const int projid=0x66;
const int gmode=0666;

#define CREATER "creater"
#define USER "user"

#define ERR_EXIT(m)\
do{\
    perror(m) ;
    exit(EXIT_FAILURE);\
}while(0)

class Shm{
private:
//创建共享内存
void CreateHelper(int shmflag)
{
    ptintf ("key:%d",_key) ;
    shmid=shmget(_key,_size,shmflag);
    if ( _shmid<6)
        ERR_EXIT( "shmget");
    printf("shmid: %d\n",_shmid);
}

void Create()
{
    CreateHelper(IPC_CREAT| IPC_EXCL | gmode);
}

void Get()
{
    CreateHelper(IPC_CREAT);
}

//将共享内存挂接到进程的地址空间上49
void Attach()
{
    start_mem=shmat( _shmid , nullptr,0) ;
    if ( (long long) start_mem<0)
        ERREXIT("shmat");
    printf( "Attach Sucess!Nn");

}

//删除与共享内存的关联关系
void DeAttach()
{
    int n=shmdt(_start_mem);
    if(n<0)
        ERR_EXIT("shmdt") ;
    else if(n==0)
        printf( "Deattach success! \n");
}

/删除共享内存
void Destory( )
{
    //删除与共享内存的关联
    DeAttach( );
    //删除共享内存
    int n=shmctl( _shmid,IPC_RMID , nullptr);
    if(n<0)
        ERR_ EXIT( "shmctl");
    printf ( "shmctl delete %dsucess!in",_shmid);
}

public:
Shm(std::string& pathname , int projid,std::string& usertype):
shmid(gdefaultid),
_size(gsize),
_start_mem(nullptr),
_usertype(usertype)
{
key=ftok(pathname.c_str( ),projid);
if(_key<0)    
    ERR_EXIT("ftok");
if(_usertype==CREATER)
{
    Create( );
}
else if (_usertype==USER)
{
    Get( );
}

Attach( );
}

//获取虚拟地址
void* VirturalAddr( )
{
    printf("virtual address: %p\n",_start_mem);106
    return_start_mem;
}

//获取共享内存大小
int Size()
    return_size;

void Attr( )
{
    struct shmid_ds ds ; //描述共享内存的结构体
    int n=shmctl(_shmid,ICP_RMID , &ds ) ;
    printf( "key : %d\n",_key);
}
~Shm( )
{
if(_usertype==CREATER)
    Destory ( );
}
private:
key_t_key;
int_shmid;
int _size;
void*_start_mem; //虚拟地址
std: :string  _usertype;
};

6.共享内存的优缺点

优点缺点
映射后读写直接被对方看到,不需要进行系统调用获取或写入内容,是进程间通信速度最快的通信双方没有所谓的“同步机制”,对共享内存数据没有保护机制

7.匿名管道 VS 命名管道 VS 共享内存 VS消息队列

特性共享内存命名管道(FIFO)匿名管道消息队列
数据传输速度最快(直接内存访问)较慢(需要内核缓冲)较慢(需要内核缓冲)适中(需要内核缓冲)
通信范围任意进程间任意进程间(有名字)具有亲缘关系的进程间任意进程间
同步机制需要额外机制(如信号量)内置同步机制(如阻塞/非阻塞)内置同步机制(如阻塞/非阻塞)内置同步机制(如消息类型)
生命周期随内核,需手动删除随内核,需手动删除随进程结束而关闭随内核,需手动删除
适用场景频繁通信、大数据量传输进程间通信、跨网络通信简单进程间通信异步通信、消息传递

http://www.dtcms.com/a/107692.html

相关文章:

  • 批量删除 txt/html/json/xml/csv 等文本文件中的重复行
  • 2025年Axure RP9无法免费使用Axure Cloud的解决方案
  • IEOSE北京国际教育留学展 留学盛宴4月亮相国会
  • c++进阶之----orderedmap和unorderedmap
  • 不同向量数据库(FAISS / Pinecone / Weaviate)在 RAG 中的优缺点
  • 生成式AI应用带来持续升级的网络安全风险
  • Linux系统CentOS 6.3安装图文详解
  • 嵌入式调试进阶:从手动到自动的HardFault破案指南
  • qt实现功率谱和瀑布图
  • Kubernetes APIServer 可观测最佳实践
  • Telnet协议详解:本质与操作逻辑
  • 路由协议分类精讲
  • TrollStore(巨魔商店)介绍及操作手册
  • C 标准库 - `<ctype.h>`
  • Vue el-table-column内el-tooltip识别换行符 \n
  • Mysql的安装
  • java 使用 spring AI 实战MCP
  • centos-LLM+EmbeddingModel+VectorDB-简单模型
  • Aliee,Bengio and Theis:细胞数据上的因果机器学习
  • 代理模式-spring关键设计模式,bean的增强,AOP的实现
  • Spring SpringBoot 细节总结
  • 【ROS】 CMakeLists 文件详解
  • 大数据Spark(五十六):Spark生态模块与运行模式
  • 《 C语言中的变长数组:灵活而强大的特性》
  • 【git项目管理】长话短说
  • JVM生产环境问题定位与解决实战(六):总结篇——问题定位思路与工具选择策略
  • 如何给槽函数传递用户的参数
  • Vue3的组件通信
  • 博卡软件管理中心8:赋能美容美发行业数字化转型的智能解决方案
  • TensorFlow实现逻辑回归