• [Linux]进程间通信--共享内存


    [Linux]system V共享内存


    共享内存是Linux系统下的一种进程间通信手段,其符合system V标准。System V标准下的通信手段接口在使用方法上是相似的,包括信号量、共享内存和消息队列这三种通信机制,

    共享内存通信的原理

    共享内存是操作系统在内存中申请的一块空间,进程将该内存映射到自身进程地址空间的共享区,然后通过这个映射往共享内存写入或读取数据,完成进程间的通信。

    image-20230910185150016

    完成通信后,进程要先解除映射关系,然后让操作系统释放共享内存。

    系统接口

    创建共享内存接口

    Linux系统中提供了shmget接口用于创建共享内存:

    //shmget所在的头文件和声明
    #include 
    #include 
    
    int shmget(key_t key, size_t size, int shmflg);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 成功返回共享内存的编号shmid,失败返回-1,错误码被设置。

    key参数

    共享内存是操作系统在内存中申请的一块内存空间,操作系统中可能会有大量的共享内存,操作系统为了管理这些共享内存就要用相应的结构进行描述,然后将描述的结构组织起来,共享内存中存在着一个字段用于唯一标识共享内存,这个字段使用的就是key参数传入的值。在进程使用共享内存通信前,一个进程创建共享内存并将key参数写入,然后另一个想要与其通信的进程就用同样的key参数和操作系统中的共享内存描述结构中的key参数进行比对,找到对应的共享内存,从而进行通信。

    image-20230910193529432

    为了让使用的key参数唯一,保证进程使用的共享内存不会出错,我们使用ftok系统接口:

    //ftok所在的头文件和声明
    #include 
    #include 
    
    key_t ftok(const char *pathname, int proj_id);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • ftok会根据传入的参数使用算法返回一个重复率极低的数字。
    • 成功返回0,失败返回-1,错误码被设置。

    size参数

    size参数用于指明要创建的共享内存的大小,单位为字节。

    操作系统创建共享内存是以page页为单位的,大小为4KB。

    shmflg参数

    shmflg参数用于指明shmget的使用模式。

    • IPC_CREAT:创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回。
    • IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
    • IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 – 如果创建成功,对应的shm,一定是最新的!
    • 传入umask可以给要创建的共享内存设置权限。

    关联共享内存接口

    要想使用共享内存必须进行共享内存的关联,将共享内存映射到自己的进程地址空间。Linux系统中提供了shmat接口用于关联共享内存:

    //shmat所在的头文件和声明
    #include 
    #include 
    
    void *shmat(int shmid, const void *shmaddr, int shmflg);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • shmid参数 – 用于指明要关联的共享内存编号。
    • shmaddr参数 – 指明要关联到的地址处,传入空指针操作系统会自己进行关联。
    • shmflg参数 – 指明对要关联的共享内存的权限,传入0为读写权限。
    • 成功返回共享内存映射在进程地址空间中的地址,失败返回-1,错误码被设置。

    去关联共享内存接口

    在使用完共享内存进行通信后,要进行共享内存的去关联操作。Linux系统中提供了shmdt接口用于去关联共享内存:

    //shmdt所在的头文件和声明
    #include 
    #include 
    
    int shmdt(const void *shmaddr);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • shmaddr参数 – 要去关联的共享内存的地址,即调用shmat关联共享内存时返回的地址。
    • 成功返回0,失败返回-1,错误码被设置。

    删除共享内存接口

    Linux系统中提供了shmctl接口用于控制共享内存:

    //shmctl所在的头文件和声明
    #include 
    #include 
    
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • shmid参数 – 用于指明要控制的共享内存编号。
    • cmd参数 – 用于指定控制选项,其中IPC_RMID选项就是删除共享内存。
    • buf参数 – 输出型参数,用于接收描述共享内存的结构体信息。

    使用指令操作共享内存

    查看共享内存

    在Linux系统中使用ipcs -m可以查看系统中的共享内存:

    image-20230910203751843

    • key:进程调用shmget创建共享内存时传入的key参数
    • shmid:共享内存编号。
    • owner:共享内存的拥有者。
    • perms:拥有者对共享内存的权限。
    • bytes:共享内存的大小。

    删除共享内存

    在Linux系统中使用ipcrm -m 对应shmid可以删除对应的共享内存:

    image-20230910204051386

    共享内存的特性

    • 无需多余拷贝:使用共享内存通信不需要使用任何接口,只要共享内存被映射到进程的地址空间中,进程就能看到共享内存。
    • 速度快 :共享内存被映射到进程的地址空间中,进程就能看到共享内存,不涉及缓冲区,无需多余拷贝动作,因此共享内存通信速度很快。
    • 无保护:使用共享内存通信不需要使用任何接口,因此共享内存不存在任何保护机制。

    编码测试共享内存

    在编码测试共享内存前,创建三个文件:common.hppserver.ccclient.cc

    common.hpp用于实现使用共享内存的类和接口,具体内容如下:

    #ifndef __COMN_HPP__
    #define __COMM_HPP__
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    #define PATHNAME "."
    #define PROJID 0x6666
    
    #define gsize 4096
    
    //key_t类型实际也是int类型
    key_t GetKey()//获取key值
    {
        key_t k = ftok(PATHNAME, PROJID);
        if(k == -1)//错误检测
        {
            cerr << "errno: " << errno << " strerror: " << strerror(errno) << endl;
            exit(1);
        }
        return k;
    }
    
    //只在本文件生效
    static int createShmHelper(key_t key, size_t size, int shmflg)
    {
        int shmid = shmget(key, size, shmflg);
        if(shmid == -1)//错误检测
        {
            cerr << "errno: " << errno << " strerror: " << strerror(errno) << endl;
            exit(2);
        }
        return shmid;
    }
    
    int createShm(key_t k, int size)
    {
        umask(0);
        return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666);
    }
    
    int getShm(key_t k, int size)
    {
        return createShmHelper(k, size, IPC_CREAT);
    }
    
    void* attachShm(int shmid)
    {
        void* start = shmat(shmid, NULL, 0);
        return start;
    }
    
    void detachShm(void* start)
    {
        int n = shmdt(start);
        assert(n != -1);
        (void)n;
    }
    
    void delShm(int shmid)
    {
        int n = shmctl(shmid, IPC_RMID, nullptr);
        assert(n != -1);
        (void)n;
    }
    
    #define SERVER 1
    #define CLIENT 0    
    
    class  Shm
    {
    public:
        Shm(int type):_type(type)
        {
            key_t key = GetKey();
            if (_type == SERVER) _shmid = createShm(key, gsize);
            else _shmid = getShm(key, gsize);
            _start = attachShm(_shmid);
        }
        void* getStart()
        {
            return _start;
        }
        ~Shm()
        {
            detachShm(_start);
            if (_type == SERVER) delShm(_shmid);
        }
    private:
        void* _start;
        int _shmid;
        int _type;
    };
    
    #endif
    
    • 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
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    server.cc用于创建共享内存,读取共享内存中的数据,完成共享内存的释放,具体内容如下:

    #include "common.hpp"
    
    int main()
    {
        Shm shm(SERVER);
        char* start = (char*)shm.getStart();
    
        int n = 0;
        while(n < 30)
        {
            cout << "client send me : " << start << endl;
            n++;
            sleep(1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    client.cc用于向共享内存写入数据,具体内容如下:

    #include "common.hpp"
    
    int main()
    {
        Shm shm(CLIENT);
        char* start = (char*)shm.getStart();
    
        char ch = 'a';
        while(ch <= 'z')
        {
            start[ch - 'a'] = ch;
            ch++;
            start[ch - 'a'] = 0;
            sleep(1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    编译代码运行并查看结果:

    共享内存

  • 相关阅读:
    基于51单片机电子钟温度计数码显示设计( proteus仿真+程序+设计报告+讲解视频)
    煤矿皮带运行视频监控系统
    Android学习笔记 43. RX思维的魅力
    SpEL 注入 学习笔记
    雷电模拟器上使用第一个frida(二)之su超级权限
    fscan使用方法:一键自动化、全方位漏洞扫描的开源工具
    解决“distribution shift”的问题
    意大利储能公司【Energy Dome】完成1500万欧元融资
    如何对Meta标签进行优化呢?
    OCR多语言识别模型构建资料收集
  • 原文地址:https://blog.csdn.net/csdn_myhome/article/details/132806063