• [Linux](13)system V 共享内存


    system V 共享内存

    共享内存的原理很简单,取物理内存的一块空间,将它通过页表映射到多个进程的共享区,这样多个进程就可以看到同一块物理内存了,这块内存就叫做共享内存。

    基本操作

    1. 创建共享内存
    2. 删除共享内存
    3. 关联共享内存
    4. 去关联共享内存

    创建共享内存

    shmget 函数

    分配共享内存

    NAME
           shmget - allocates a System V shared memory segment
    
    SYNOPSIS
           #include 
           #include 
    
           int shmget(key_t key, size_t size, int shmflg);
    // 返回值:成功:返回有效的共享内存标识符。错误:返回-1,并设置errno以指示错误。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • key:共享内存的唯一值,自己通过函数ftok设置

      • NAME
               ftok - convert a pathname and a project identifier to a System V IPC key
        
        SYNOPSIS
               #include 
               #include 
        
               key_t ftok(const char *pathname, int proj_id);
        // 返回值:成功:返回生成的key_t值,失败:返回-1
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
    • size:指定大小(byte),建议设置为页(PAGE_SIZE = 4KB)的整数倍

    • shmflg:用于区分共享内存已经存在和不存在时的做法,其有两个选项

      • IPC_CREAT:共享内存如果已经存在,就获取之,如果不存在,就创建之

      • IPC_EXCL:与 IPC_CREAT 配合使用,如果不存在指定的共享内存,创建之,如果存在,出错返回。该选项可以保证如果shmget函数调用成功,则一定创建了一个全新的共享内存。

      • 此参数还可以直接按位或一个八进制来指定权限

    注意:system V 下的共享内存,生命周期是随内核的,创建的共享内存在进程退出后依然存在。

    例子

    Comm.hpp

    包含各种头文件,创建key的相关设置

    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    #define PATH_NAME "/home/CegghnnoR/code"
    #define PROJ_ID 0x18
    #define MEM_SIZE 4096
    
    key_t CreateKey()
    {
        key_t key = ftok(PATH_NAME, PROJ_ID);
        if (key < 0)
        {
            cerr << strerror(errno) << endl;
            exit(1);
        }
        return key;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    Log.hpp

    设置输出格式

    #pragma once
    
    #include 
    #include 
    using namespace std;
    
    ostream& Log()
    {
        cout << "For Debug | " << "timestamp: " << (uint64_t)time(nullptr) << "| ";
        return cout;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    IpcShmSer

    服务端代码,创建共享内存

    #include "Comm.hpp"
    #include "Log.hpp"
    
    // 表示创建全新的共享内存
    const int flags = IPC_CREAT | IPC_EXCL;
    int main()
    {
        key_t key = CreateKey();
        Log() << "key:" << key << endl;
        int shmid = shmget(key, MEM_SIZE, flags);
        if (shmid < 0)
        {
            Log() << "shmget: " << strerror(errno) << "\n";
            return 2;
        }
    
        Log() << "create shm success, shmid: " << shmid << "\n";
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    实验结果

    [CegghnnoR@VM-4-13-centos 2022_11_4]$ ./IpcShmSer
    For Debug | timestamp: 1667631442| key:402719453
    For Debug | timestamp: 1667631442| create shm success, shmid: 1
    [CegghnnoR@VM-4-13-centos 2022_11_4]$ ./IpcShmSer
    For Debug | timestamp: 1667631551| key:402719453
    For Debug | timestamp: 1667631551| shmget: File exists
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第一次运行可以成功创建共享内存,进程退出后再运行一次,发现创建失败了,说明第一次运行后,原来的共享内存依然存在。

    查看共享内存列表

    使用 ipcs -m 命令可以查看当前已创建的共享内存

    [CegghnnoR@VM-4-13-centos 2022_11_4]$ ipcs -m
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status      
    0x00005feb 0          root       666        12000      1                       
    0x180102dd 1          CegghnnoR  0          4096       0                       
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其中 perms 为访问权限,nattch 表示与此共享内存关联的进程数。

    删除共享内存

    指令删除

    ipcrm -m [shmid]用于删除共享内存

    [CegghnnoR@VM-4-13-centos 2022_11_4]$ ipcrm -m 1
    [CegghnnoR@VM-4-13-centos 2022_11_4]$ ipcs -m
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status      
    0x00005feb 0          root       666        12000      1    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    系统调用接口删除

    NAME
           shmctl - System V shared memory control
    
    SYNOPSIS
           #include 
           #include 
    
           int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    仅仅作删除共享内存使用,指定相应的 shmidcmd 指定为 IPC_RMIDbuf 指定为 nullptr

    关联与去关联共享内存

    NAME
           shmat, shmdt - System V shared memory operations
    
    SYNOPSIS
           #include 
           #include 
    
           void *shmat(int shmid, const void *shmaddr, int shmflg);
    // 返回值:成功:返回附加的共享内存段的地址;失败:返回(void*)-1,并设置errno以指示错误的原因。
           int shmdt(const void *shmaddr);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    shmat

    • shmid 指定你要关联的共享内存,shmaddr 我们设为 nullptrshmflg 设为 0

    • 共享内存的使用,和直接用malloc出来的空间的使用相似(除了free)。

    shmdt

    • 去关联共享内存,其参数 shmaddr 就是 shmat 的返回值

    服务端:

        // 关联共享内存
        char* str = (char*)shmat(shmid, nullptr, 0);
        Log() << "attach shm: " << shmid << " success\n";
    
        // 使用...
    
        // 去关联
        shmdt(str);
        Log() << "detach shm: " << shmid << " success\n";
        // 删除共享内存
        shmctl(shmid, IPC_RMID, nullptr);
        Log() << "delete shm: " << shmid << " success\n";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    客户端:

    服务端创建的共享内存服务端删除,客户端不用删

        // 关联
        char* str = (char*)shmat(shmid, nullptr, 0);
        // 使用...
    
        // 去关联
        shmdt(str);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用共享内存

    共享内存的使用非常简单,shmat 返回的地址就是共享内存的地址,我们可以直接使用指针访问块空间,且由于连个进程共享这一块空间,所以其中一方写,另一方立刻就能看到。

    完整代码

    Comm.hpp

    #pragma once
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    #define PATH_NAME "/home/CegghnnoR/code"
    #define PROJ_ID 0x18
    #define MEM_SIZE 4096
    
    key_t CreateKey()
    {
        key_t key = ftok(PATH_NAME, PROJ_ID);
        if (key < 0)
        {
            cerr << strerror(errno) << endl;
            exit(1);
        }
        return key;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    Log.hpp

    #pragma once
    
    #include 
    #include 
    using namespace std;
    
    ostream& Log()
    {
        cout << "For Debug | " << "timestamp: " << (uint64_t)time(nullptr) << "| ";
        return cout;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    IpcShmSer.cpp

    循环读取共享内存中的字符串

    #include "Comm.hpp"
    #include "Log.hpp"
    
    // 表示创建全新的共享内存
    const int flags = IPC_CREAT | IPC_EXCL;
    int main()
    {
        key_t key = CreateKey();
        Log() << "key:" << key << endl;
        int shmid = shmget(key, MEM_SIZE, flags | 0666);
        if (shmid < 0)
        {
            Log() << "shmget: " << strerror(errno) << "\n";
            return 2;
        }
    
        Log() << "create shm success, shmid: " << shmid << "\n";
    
        // 关联共享内存
        char* str = (char*)shmat(shmid, nullptr, 0);
        Log() << "attach shm: " << shmid << " success\n";
    
        // 使用...
        while (true)
        {
            printf("%s\n", str);
            sleep(1);
        }
    
        // 去关联
        shmdt(str);
        Log() << "detach shm: " << shmid << " success\n";
        // 删除共享内存
        shmctl(shmid, IPC_RMID, nullptr);
        Log() << "delete shm: " << shmid << " success\n";
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    IpcShmCli.cpp

    向共享内存写入ABCD…

    #include "Comm.hpp"
    #include "Log.hpp"
    
    int main()
    {
        // 获取相同的key值
        key_t key = CreateKey();
        Log() << "key:" << key << endl;
        // 获取共享内存
        int shmid = shmget(key, MEM_SIZE, IPC_CREAT);
        if (shmid < 0)
        {
            Log() << "shmget: " << strerror(errno) << "\n";
            return 2;
        }
        // 关联
        char* str = (char*)shmat(shmid, nullptr, 0);
        // 使用...
        int cnt = 0;
        while (cnt <= 26)
        {
            str[cnt] = 'A' + cnt;
            ++cnt;
            str[cnt] = '\0';
            sleep(1);
        }
        // 去关联
        shmdt(str);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行结果

    gongxiangneicun

    从结果也很容易发现,共享内存没有任何访问控制,即读端会直接读取,不管共享内存中有没有数据,一切都是原生的指针访问。

    共享内存,是所有进程间通信渠道中,速度最快的,但是不安全。

    概念

    • 能够被多个进程所访问的资源被称为临界资源
    • 如果对临界资源没有进行任何保护,在对临界资源进行访问的时候,就是乱序的,可能会因为读写交叉导致各种乱码、废弃数据等访问控制方面的问题。
    • 对多个进程而言,访问临界资源的代码被称为临界区
  • 相关阅读:
    Word处理控件Aspose.Words功能演示:使用 Java 将文本转换为 PDF
    js 找出两个数组中的重复元素
    【教师资格证考试综合素质——法律专项】教育法笔记以及练习题
    C++入门之命名空间详解
    PIES源码,大型体检中心源码,医院智慧体检系统源码
    如何看待AIGC技术?
    2022.7.25-7.31 AI行业周刊(第108期):值钱比赚钱更重要
    css案例16——ul无序列表(无li时无样式,有li后出现样式)
    亿赛通电子文档安全管理系统 Update.jsp SQL注入
    点云数据结构化与体素化理论学习
  • 原文地址:https://blog.csdn.net/CegghnnoR/article/details/127716260