Linux:进程间通信-System V 共享内存

一、共享内存(what)的原理

进程A和进程B需要通信,两者需要看到同一块地方(让不同的进程看到同一个资源)。在进程的虚拟地址中,有共享区,可以让两个进程的共享区通过页表指向同一块物理空间,这样就实现了进程之间的通信。

共享内存也可能是有许多个的,所以需要组织管理,所以就需要先描述(结构体),再组织。

所以:共享内存 = 结构体 + 内存块

二、共享内存使用

1.创建

2.关联挂接

3.使用

4.去关联

5.释放共享内存

1.创建

shmget 函数

bash 复制代码
NAME
       shmget - allocates a System V shared memory segment

LIBRARY
       Standard C library (libc, -lc)

SYNOPSIS
       #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 error, -1 is returned, and errno is set to indicate the error.

**作用:**申请一块内存空间

参数:

key:共享内存的key值,在内核中,用来标识共享内存的唯一性,需要用户来传(用ftok函数来生成)。

细节:这里的key值为什么不能让os自己生成呢?

答:进程A生成内存块,那么进程A可以得到key值,进程B怎么可以得到呢?

进程B要得到,进程之间就需要进程间通信,???虽然可以通过匿名管道或者命名管道获得,但是要实现一种通信还需要依靠其它通信方法,太糟糕了。

**size:**申请空间的大小,是4096的整数倍

shmflg:传标志位和权限(用 | 来合并)

下面介绍2个标志位

bash 复制代码
IPC_CREAT
              Create  a  new segment.  If this flag is not used, then shmget() will find the seg‐
              ment associated with key and check to see if the user has permission to access  the
              segment.

IPC_EXCL
              This  flag is used with IPC_CREAT to ensure that this call creates the segment.  If
              the segment already exists, the call fails.

**IPC_EXCL:**不能单独使用

**IPC_CREAT:**可以单独传递,如果创建的共享内存不存在,就创建,如果存在,就获取它、

(可以用来获取shmid)

**IPC_CREAT | IPC_EXCL:**如果创建的共享内存不存在,就创建,如果存在,出错返回

(创建共享内存)

返回值:

bash 复制代码
On  success,  a valid shared memory identifier is returned.  On error, -1 is returned, and errno is set to indicate the error.

成功:返回内存块的身份标识 shmid,这个身份标识用户可以来使用

失败:返回 -1

ftok 函数

bash 复制代码
NAME
       ftok - convert a pathname and a project identifier to a System V IPC key

LIBRARY
       Standard C library (libc, -lc)

SYNOPSIS
       #include <sys/ipc.h>

       key_t ftok(const char *pathname, int proj_id);
RETURN VALUE
       On  success, the generated key_t value is returned.  On failure -1 is returned, with errno
       indicating the error as for the stat(2) system call.

作用:生成key值

参数

pathname:一个字符串,最好是存在的路径

proj_id:一个数字,用户设置,用来生成这个共享内存的key值

其实就是通过一些计算生成一个key值

细节:多个共享内存的key值有可能重复吗。

答:有可能,不过shget函数会有调用错误

命令:查看共享内存块

bash 复制代码
ipcs 
bash 复制代码
zhangsan@hcss-ecs-f571:~$ ipcs

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     
bash 复制代码
ipcs -m
bash 复制代码
zhangsan@hcss-ecs-f571:~$ ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status  

命令:删除

bash 复制代码
zhangsan@hcss-ecs-f571:~$ ipcrm -m shmid

细节:用户不主动删除ipc资源的话,ipc的生命周期随OS,会一直存在,除非重启操作系统

理解shmid和key值

1.key只在内核中,标识共享内存的唯一性!用户使用共享内存,不使用key值

2.shmid 在用户中使用,使用shmid来访问共享内存

(了解过文件系统的话 可以类比文件描述符fd和inode的关系)

2.控制、删除

shmctl 函数(shell memeny control)

bash 复制代码
NAME
       shmctl - System V shared memory control

LIBRARY
       Standard C library (libc, -lc)

SYNOPSIS
       #include <sys/shm.h>

       int shmctl(int shmid, int op, struct shmid_ds *buf);

**作用:**可以控制shmid共享内存块(删除,获取,设置共享内存的属性)。

参数:

**shmid:**共享内存块的id

**op:**操作方式

IPC_RMID 用删除内存块

其他操作可以查看手册。

**buf:**共享内存块中结构体中的内容(属性)

cpp 复制代码
struct shmid_ds {
    struct ipc_perm shm_perm;	/* 操作权限 */
    int shm_segsz;		/* 共享内存段大小 */
    __kernel_time_t shm_atime;	/* 最后一次attach时间 */
    __kernel_time_t shm_dtime;	/* 最后一次detach时间 */
    __kernel_time_t shm_ctime;	/* 最后一次change时间 */
    __kernel_ipc_pid_t shm_cpid;	/* 创建者pid */
    __kernel_ipc_pid_t shm_lpid;	/* 最后操作的pid */
    unsigned short shm_nattch;	/* 当前attach的进程数 */
    unsigned short shm_unused;	/* 保留字段 */
    void *shm_unused2;		/* 保留字段 */
    void *shm_unused3;		/* 保留字段 */
};

删除时可以设置为nullptr

3.关联挂接、去关联

shmat 函数(attach)

cpp 复制代码
NAME
       shmat, shmdt - System V shared memory operations

LIBRARY
       Standard C library (libc, -lc)

SYNOPSIS
       #include <sys/shm.h>

       void *shmat(int shmid, const void *_Nullable shmaddr, int shmflg);

**作用:**将共享内存链接到进程虚拟地址空间

参数:

**shmid:**共享内存块的id

**shmaddr:**指定链接的地址(这里一般让os自己来决定,设置为nullptr)

**shmflg:**关联的权限(这里可以设置为0,因为shmget创建共享内存块的时候可以把权限设置好,这里有特殊要求可以查看参考手册来设置)

**返回值:**成功,返回一个指针,指向共享内存第一个节;失败,返回 -1

shmdt 函数 (detach)

cpp 复制代码
NAME
       shmat, shmdt - System V shared memory operations

LIBRARY
       Standard C library (libc, -lc)

SYNOPSIS
       #include <sys/shm.h>

       int shmdt(const void *shmaddr);

**作用:**将共享内存段与当前进程脱离

参数:shmat的返回值

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

三、共享内存的优缺点

  • 访问特性:访问共享内存,不需要系统调用,因为 shm 已经映射到了进程的用户共享区了!
  • 实时性:写端数据拷贝到 shm,其他端立马能看到!!
  • 性能优势 :共享内存,是所有进程间通信方式中,速度最快的!!
    • 底层原理:拷贝次数少,直接映射,不需要系统调用!
  • 缺点 :没有资源的保护机制,没有同步或者互斥!
    • 补充说明:sem(信号量)由用户自己完成保护!

四、小demo,用共享内存实现两个进程之间的通信

Shm.hpp

cpp 复制代码
#pragma once
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <sys/shm.h>

const int gsize = 128;
// 用户指明,本质:等价于;命名管道里面的文件路径
// PATHNAME, PROJ_ID:我们两个就能看到同一份资源!
#define PATHNAME "/tmp"
#define PROJ_ID 0x66

class Shm
{

public:
    Shm():_shmid(-1),_size(gsize),_start_addr(nullptr)
    {}
    ~Shm()
    {}

    void Delete()
    {
        int n = shmctl(_shmid, IPC_RMID, nullptr);
    }
    // void Get()
    // {
    //      key_t k = Getkey();
    //     if(k < 0)
    //     {
    //         std::cerr << "Getkey error";
    //         exit(1);
    //     }
    //     printf("key = 0x%x,key = %d\n", k, k); 
    //     // _shmid = shmget(k,_size,IPC_CREAT); 
    // }
    void Attach()
    {
        _start_addr = shmat(_shmid, nullptr, 0);
        if(_start_addr == (void*)(-1)) exit(3);
    }
    void PrintAttr()
    {
        struct shmid_ds ds;
        int n = shmctl(_shmid, IPC_STAT, &ds);
        if(n < 0)
        {
            perror("shmctl");
            exit(4);
        }
        printf("key:0x%x\n", ds.shm_perm.__key);
        printf("shm_nattch:%ld\n", ds.shm_nattch);
        printf("segsz:%ld\n", ds.shm_segsz);

    }
    void Detach()
    {
        shmdt(_start_addr);
    }
    void Get()
    {
        GetHelper(IPC_CREAT);
    }
    void Create()
    {
        GetHelper(IPC_CREAT | IPC_EXCL | 0666);
    }
    void* Addr()
    {
        return _start_addr;
    }
    int Size()
    {
        return _size;
    }
private:
    void GetHelper(int shmflg)
    {   
        //1.构建键值
        key_t k = Getkey();
        if(k < 0)
        {
            std::cerr << "Getkey error";
            exit(1);
        }
        printf("key = 0x%x,key = %d\n", k, k); 
        //2.创建新的共享内存
        _shmid = shmget(k,_size,shmflg); 
        if(_shmid < 0)
        {
            perror("shmget");
            exit(2);
        }
        printf("key = 0x%x,key = %d,_shmid = 0x%x\n", k, k, _shmid); 
    }

    key_t Getkey()
    {
        return ftok(PATHNAME, PROJ_ID);
    }
private:
    int _shmid; 
    int _size;
    void* _start_addr;
};

Server.cc

cpp 复制代码
#include <iostream>
#include "Shm.hpp"
int main()
{
    Shm sharedmem;
    sharedmem.Create();
    sharedmem.Attach();
    sleep(2);
    sharedmem.PrintAttr();
    char *shm_start = (char *)sharedmem.Addr();
    int sz = sharedmem.Size();
    while (1)
    {
        // 本质是读取共享内存
        for (int i = 0; i < sz; i++)
        {
            std::cout << shm_start[i] << " ";
        }
        std::cout << std::endl;
        sleep(1);
    }
    sharedmem.Detach();
    sharedmem.Delete();

    return 0;
}

Client.cc

cpp 复制代码
#include <iostream>
#include "Shm.hpp"
int main()
{
    Shm sharedmem;
    sharedmem.Get();
    sharedmem.Attach();
    sleep(2);
    sharedmem.PrintAttr();
    char* shm_start = (char*)sharedmem.Addr();
    int sz = sharedmem.Size();
    int index = 0;
    while(true)
    {
        std::cout << "please Enter@ ";
        char ch;
        std::cin >> ch;
        *(shm_start + index) = ch;
        shm_start[index++] = ch;
        index %= sz;
        sleep(1);
    }
 
    sharedmem.Detach();
    return 0;
}
相关推荐
木子欢儿2 小时前
Ubuntu 24.04 执行超微服务器 JNLP 程序
linux·运维·服务器·ubuntu
柠檬味的Cat2 小时前
腾讯云轻量服务器一键部署OpenClaw教程
服务器·腾讯云
我不是立达刘宁宇2 小时前
测试哥斯拉的使用
运维
还在忙碌的吴小二2 小时前
在 Mac 上安装并通过端口调用 Chrome DevTools MCP Server(谷歌官方 MCP 服务器)
服务器·前端·chrome·macos·chrome devtools
TechMasterPlus2 小时前
agent-browser 技术深度解析:Vercel 推出的 AI 时代浏览器自动化利器
运维·人工智能·自动化
_下雨天.7 小时前
LVS负载均衡
服务器·负载均衡·lvs
小成2023032026510 小时前
Linux高级02
linux·开发语言
mounter62510 小时前
【硬核前沿】CXL 深度解析:重塑数据中心架构的“高速公路”,Linux 内核如何应对挑战?-- CXL 协议详解与 LSF/MM 最新动态
linux·服务器·网络·架构·kernel
++==10 小时前
Linux 进程间通信与线程同步技术详解:IPC 机制、线程 API、同步工具与经典同步问题
linux