【Linux】进程间通信——system V(共享内存 | 消息队列 | 信号量)(上)

简介: 【Linux】进程间通信——system V(共享内存 | 消息队列 | 信号量)(上)

> 作者:დ旧言~

> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:理解进程通信——system V(共享内存 | 消息队列 | 信号量)

> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!

> 专栏选自:Linux初阶

> 望小伙伴们点赞👍收藏✨加关注哟💕💕



🌟前言

system V:同一主机内的进程间通信方案,在OS层面专门为进程间通信设计的方案

进程间通信的本质:让不同的进程看到同一份资源

system V标准下的三种通信方式

  1. 共享内存
  2. 消息队列
  3. 信号量


我们来看看 system V标准下的三种通信方式 。


⭐主体

学习【Linux学习】进程间通信——system V(共享内存 | 消息队列 | 信号量)咱们按照下面的图解:



🌙 共享内存

💫 共享内存的基本原理

概念:

共享内存:通过让不同的进程,看到通过一个内存块的方式就叫共享内存。

进程具有独立性:

内核数据结构包括对应的代码、数据与页表都是独立的。OS系统为了让进程间进行通信:


1.申请一块空间

2.将创建好的内存映射进进程的地址空间

  • 共享内存让不同的进程看到同一份的资源就是在物理内存上申请一块内存空间,如何将创建好的内存分别与各个进程的页表之间建立映射,然后在虚拟地址空间中将虚拟地址填充到各自页表的对应位置,建立起物理地址与虚拟地址的联系。



3.如果不想通信:取消进程和内存的映射关系,释放内存

  • 而我们把创建好的内存称为共享内存,把进程和共享内存建立映射关系的操作称为挂接,把取消进程和内存的映射关系称为去关联
  • 把释放内存称为释放共享内存。
  • 共享内存的建立:在物理内存当中申请共享内存空间;将申请到的共享内存挂接到地址空间,即建立映射关系。
  • 共享内存的释放:共享内存与地址空间去关联,即取消映射关系;释放共享内存空间,即将物理内存归还给系统。


对于共享内存的理解:

对比以前C语言的malloc也可以在物理内存申请空间,并把开辟好的空间经过页表映射到进程地址空间当中。

但是system V进程间通信是专门设计的,用来IPC;共享内存是一种通信方式,所有想通信的进程都可以进行使用;OS一定可能会同时存在很多的共享内存。


💫 共享内存的创建

shmget:用来创建共享内存

#include <sys/ipc.h>
#include <sys/shm.h>
 
int shmget(key_t key, size_t size, int shmflg);
 
RETURN VALUE
       On success, a valid shared memory identifier is returned.  On errir, -1 is returned, and errno is set to indicate the error.


参数讲解:

  • shmflg:通常被设置成两个选项: IPC_CREAT、 IPC_EXCL

IPC_CREAT:共享内存不存在,则创建,如果存在则获取;IPC_EXCL:无法单独使用,IPC_CREAT|IPC_EXCL:如果不存在就创建,如果存在就出错返回


  • size:共享内存的大小
  • key:保证看到同一份共享内存,能进行唯一性标识。如何去形成key:ftok


ftok:形成key

ftok作用:是通过存在的路径名pathname以及设置的标识符proj_id来形成一个key值,通过shmget创建共享内存时,key值会被填充维护共享内存的数据结构当中。


理解key:


OS一定会存在很多的共享内存,共享内存本质就是在内存中申请一块空间,而key能进行唯一标识。OS申请的,自然要做管理,共享内存也是如此,如何管理:先描述,在组织。所以共享内存=物理内存块+共享内存的相关属性。进程如果在内存中创建了共享内存,为了让共享内存在系统中保证唯一的,通过key来进行标识,只要让另一个进程也看到同一个key。而key是在哪?key作为创建共享内存时共享内存的相关属性集合,描述共享内存时就有一个字段struct shm中有key。


共享内存数据结构的第一个成员是shm_perm,shm_perm是一个ipc_perm类型的结构体变量,每个共享内存的key值存储在shm_perm这个结构体变量当中,其中ipc_perm结构体的定义如下:

    struct ipc_perm {
               key_t          __key;    /* Key supplied to shmget(2) */
               uid_t          uid;      /* Effective UID of owner */
               gid_t          gid;      /* Effective GID of owner */
               uid_t          cuid;     /* Effective UID of creator */
               gid_t          cgid;     /* Effective GID of creator */
               unsigned short mode;     /* Permissions + SHM_DEST and
                                           SHM_LOCKED flags */
               unsigned short __seq;    /* Sequence number */
           };
 


💫 共享内存的控制

shmctl:控制共享内存

 #include <sys/ipc.h>
 #include <sys/shm.h>
 
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
 


参数讲解:

  • shmid:共享内存id
  • cmd:控制方式,这里我们只使用IPC_RMID 选项,表示删除共享内存
  • buf:描述共享内存的数据结构

返回值:0表示成功,-1表示失败

共享内存的内核结构shmid_ds:

//man shmctl 
struct shmid_ds {
               struct ipc_perm shm_perm;    /* Ownership and permissions */
               size_t          shm_segsz;   /* Size of segment (bytes) */
               time_t          shm_atime;   /* Last attach time */
               time_t          shm_dtime;   /* Last detach time */
               time_t          shm_ctime;   /* Last change time */
               pid_t           shm_cpid;    /* PID of creator */
               pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
               shmatt_t        shm_nattch;  /* No. of current attaches */
               ...
           };


💫 共享内存的关联

shmat:关联共享内存

 #include <sys/types.h>
 #include <sys/shm.h>
 
 void *shmat(int shmid, const void *shmaddr, int shmflg);
 


参数讲解:

  • shmid:共享内存id
  • shmaddr:挂接地址(自己不知道地址,所以默认为NULL)
  • shmflg:挂接方式,默认为0

返回值:挂接成功返回共享内存起始地址(虚拟地址),类似C语言malloc

💫 共享内存的去关联

shmdt:去关联

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
 
RETURN VALUE
On  success  shmdt() returns 0; on error -1 is returned, and errno is set to indi‐cate the cause of the error.


参数讲解:

  • shmaddr:去关联内存地址,即shmat返回值
  • 返回值:调用成功返回0,失败返回-1


💫 查看IPC资源

概念:

对于管道:进程退出,文件描述符就会自动释放,但是对于共享内存不一样:共享内存的生命周期是随OS的,而不是随进程的,这是所有System V进程间通信的共性。


查看共享内存:ipcs -m



删除:ipcsrm -m + shmid



💫 代码实现通信

1.comm.hpp:
#ifndef __COMM_HPP_
#define __COMM_HPP_
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
#define PATHNAME "."
#define PROJ_JD 0x66
#define MAX_SIZE 4096
 
key_t getkey()
{
    key_t k = ftok(PATHNAME,PROJ_JD);
    if(k <0)
    {
        cerr<<errno<<":"<<strerror(errno)<<endl;
        exit(1);
    }
    return k;
}
 
int getShmHelper(key_t k,int flags)
{
    //k是要shmget,设置进入共享内存属性中的,用来标识
    //该共享难内存在内核中的唯一性
 
    //shmid与key:
    //fd     inode
    int shmid = shmget(k,MAX_SIZE,flags);
    if(shmid<0)
    {
        cerr<<errno<<":"<<strerror(errno)<<endl;
        exit(2);
    }
    return shmid;
} 
 
 
//获取
int getShm(key_t k)
{
    return getShmHelper(k,IPC_CREAT);
}
//创建
int createShm(key_t k)
{
    return getShmHelper(k,IPC_CREAT | IPC_EXCL|0600);
}
 
void delShm(int shmid)
{
    if(shmctl(shmid,IPC_RMID,nullptr)==-1)
    {
        cerr<<errno<<":"<<strerror(errno)<<endl;
    }
}
 
void * attachShm(int shmid)
{
    void*mem = shmat(shmid,nullptr,0);
    if((long long)mem==-1L)//64位系统,8个字节,L表示数字类型
    {
        cerr<<errno<<"shmat:"<<strerror(errno)<<endl;
        exit(3);
    }
    return mem;
}
 
void detachShm(void * start)
{
    if(shmdt(start)==-1)
    {
        cerr<<"shmdt:"<<errno<<":"<<strerror(errno)<<endl;
    }
}
 
#endif 


2.server.cc:
#include "comm.hpp"
#include <unistd.h>
using namespace std;
 
int main()
{
    key_t k = getkey();
    printf("key:%0x%x\n",k);
 
    int shmid = createShm(k);
    printf("shmid:%d\n",shmid);
 
    //sleep(5);
 
    char*start = (char*)attachShm(shmid);
    printf("attach success,address start:%p\n",start);
 
    //使用
    while(true)
    {
        printf("client say:%s\n",start);
        struct shmid_ds ds;
        shmctl(shmid,IPC_STAT,&ds);
        printf("获取属性:size:%d,pid:%d,myself:%d",ds.shm_segsz,ds.shm_cpid);
        sleep(1);
    }
 
    //去关联
    detachShm(start);
 
    sleep(10);
    //删除共享内存
    delShm(shmid);
    return 0;
}


3.client.cc:
#include "comm.hpp"
#include <unistd.h>
using namespace std;
 
int main()
{
    key_t k = getkey();
    printf("key:%0x%x\n",k);
 
    int shmid = getShm(k);
    printf("shmid:%d\n",shmid);
 
    char*start = (char*)attachShm(shmid);
    printf("attach success,address start:%p\n",start);
 
    const char*message = "hello server,我是另一个进程,正在和你通信";
    pid_t id = getpid();
    int count = 1;
    //char buffer[1024];
    while(true)
    {
       sleep(5);
       snprintf(start,MAX_SIZE,"%s[pid:%d][消息编号:%d]",message,id,count++);
 
       // snprintf(buffer,sizeof(buffer),"%s[pid:%d][消息编号:%d]",message,id,count++);
      //  memcpy(start,buffer,strlen(buffer)+1);
 
    }
 
 
    detachShm(start);
 
 
    return 0;
}




【Linux】进程间通信——system V(共享内存 | 消息队列 | 信号量)(下)    https://developer.aliyun.com/article/1565753

目录
相关文章
|
6天前
|
Linux Shell
6-9|linux查询现在运行的进程
6-9|linux查询现在运行的进程
|
8天前
|
Docker 容器
14 response from daemon: open \\.\pipe\docker_engine_linux: The system cannot find the file speci
14 response from daemon: open \\.\pipe\docker_engine_linux: The system cannot find the file speci
11 1
|
19天前
|
存储 监控 安全
探究Linux操作系统的进程管理机制及其优化策略
本文旨在深入探讨Linux操作系统中的进程管理机制,包括进程调度、内存管理以及I/O管理等核心内容。通过对这些关键组件的分析,我们将揭示它们如何共同工作以提供稳定、高效的计算环境,并讨论可能的优化策略。
22 0
|
1月前
|
Unix Linux
linux中在进程之间传递文件描述符的实现方式
linux中在进程之间传递文件描述符的实现方式
|
2月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
52 0
|
3月前
|
消息中间件 C语言 RocketMQ
消息队列 MQ操作报错合集之出现"Connection reset by peer"的错误,该如何处理
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
3月前
|
消息中间件 Java C语言
消息队列 MQ使用问题之在使用C++客户端和GBase的ESQL进行编译时出现core dump,该怎么办
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
19天前
|
消息中间件
手撸MQ消息队列——循环数组
队列是一种常用的数据结构,类似于栈,但采用先进先出(FIFO)的原则。生活中常见的排队场景就是队列的应用实例。在数据结构中,队列通常用数组实现,包括入队(队尾插入元素)和出队(队头移除元素)两种基本操作。本文介绍了如何用数组实现队列,包括定义数组长度、维护队头和队尾下标(front 和 tail),并通过取模运算解决下标越界问题。此外,还讨论了队列的空与满状态判断,以及并发和等待机制的实现。通过示例代码展示了队列的基本操作及优化方法,确保多线程环境下的正确性和高效性。
24 0
手撸MQ消息队列——循环数组
|
2月前
|
消息中间件 存储 缓存
一个用过消息队列的人,竟不知为何要用 MQ?
一个用过消息队列的人,竟不知为何要用 MQ?
91 1
|
3月前
|
消息中间件 开发工具 RocketMQ
消息队列 MQ使用问题之一直连接master失败,是什么原因
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
下一篇
无影云桌面