Linux进程间通信:共享内存详解

文章目录

  • 共享内存(Shared Memory)
  • 什么是共享内存
  • 2. 共享内存的特点
  • 3.共享内存的主要函数
  • 3.1.shmget()
  • 3.2.shmat
  • 3.3.shmdt
  • 3.4.shmctl
  • 共享内存实现进程间通信
  • ShareMemory.hpp
  • Server.cc
  • Client.cc
  • 总结
  • 共享内存(Shared Memory)

    什么是共享内存

    共享内存(Shared Memory)是一种 进程间通信(IPC) 机制,允许多个进程共享同一块物理内存,从而提高数据交换效率。相比其他 IPC 方式(如管道、消息队列等),共享内存具有 速度快、低开销 的优势,因为数据直接存储在内存中,而无需通过内核进行数据拷贝。

    2. 共享内存的特点

  • 高效:数据直接在内存中共享,避免了进程间数据拷贝的开销。
  • 进程可见:多个进程可以同时访问同一块共享内存,实现高速数据传输。
  • 需要同步机制:由于多个进程可以并发访问共享内存,通常需要使用 信号量(Semaphore)互斥锁(Mutex) 来防止数据竞争。
  • 3.共享内存的主要函数

    函数 作用
    shmget() 创建或获取一个共享内存段
    shmat() 将共享内存附加到进程地址空间
    shmdt() 解除共享内存与进程的关联
    shmctl() 控制共享内存(删除、修改权限等)

    3.1.shmget()


    shmget表示获取共享内存,第一个参数key表示共享内存的键值,用于标识唯一的共享内存段。


    这个参数由用户个人设置,但是通常用ftok函数来获取key。

    ftok函数通过一定的算法来获取相对不会重复的key值,第一个参数是路径,第二个参数随机填一个数,通过算法获取相对唯一的key值。


    shmget的第二个参数表示共享内存的大小,第三个参数表示标志位,如何创建共享内存和设置共享内存的权限。

    第三个参数有特定的宏可以选择,红框框起来的是常用的两个。
    IPC_CREAT:单独使用表示获取共享内存,如果存在则报错
    IPC_CREAT | IPC_EXCL:表示创建共享内存
    IPC_EXCL:单独使用没有意义

    3.2.shmat

    当我们获取到共享内存的时候,我们需要将共享内存挂接到虚拟内存地址当中,这时就需要用到这个接口。

    用下面简图表示挂接:

    shmget的第一个参数shmid表示shmget的返回值,会返回一个shmid,第二个参数表示我们可以指定一个虚拟地址,挂接到指定的虚拟地址上,但是一般情况下我们都会默认使用分配的虚拟地址,所以第二个参数一般情况下都会填nullptr,第三个参数表示标志位,用于控制映射方式(常用 0 或 SHM_RDONLY)。

    3.3.shmdt

    去关联,和上一个关联恰好相反,一个是关联一个是去关联。

    3.4.shmctl

    这个函数是用于控制共享内存的,在命令行我们一般用ipcrm -m shmid这个命令来删除共享内存,但是在代码层面,我们一般用shmctl这个函数来控制共享内存,可以进行删除修改权限等操作。

    第二个参数表示标志位进行什么操作,下面是可以进行的操作,红框框起来的,表示删除共享内存,我们可以用这个宏来实现删除共享内存。

    第三个参数是获取共享内存的信息,放在一个结构体当中,如果我们不需要获取直接传nullptr

    接口讲完了,接下来用共享内存实现进程间通信

    共享内存实现进程间通信

    ShareMemory.hpp

    #pragma once
    #include <iostream>
    #include <string>
    #include <sys/types.h>
    #include <cstdlib>
    #include <ctime>
    #include <unistd.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    using namespace std;
    const string gpath = "/home/llllyrics/112_class";
    int gprojid = 0X6666;
    //操作系统,申请空间,是按照块为单位的:4kb,1kb,2kb,4mb.......
    int gshmsize = 4096;
    mode_t gmode = 0600;//权限
    
    const int CREATE = IPC_CREAT | IPC_EXCL | gmode;
    const int GET = IPC_CREAT;
    
    
    class ShareMemory
    {
    private:
    void CreateShmHelper(int flag)
        {
            //创建key
            _key = ftok(gpath.c_str(),gprojid);
            if(_key < 0)//创建失败
            {
                cerr<<"ftok error"<<endl;
                return;
            }
            //让server创建共享内存&&获取
            //注意:共享内存也有权限!在应用层和文件关联度不大,但是在底层和文件关联度大
            _shmid = shmget(_key,gshmsize,flag);//创建
            if(_shmid < 0)//创建失败
            {
                cerr<<"shmget error"<<endl;
                return;
            }
        }
    public:
        ShareMemory():_shmid(-1),_key(0),_addr(nullptr){}
        ~ShareMemory(){}
    
        void CreateShm()
        {
            if(_shmid == -1)
                CreateShmHelper(CREATE);
        }
        void GetShm()
        {
            CreateShmHelper(GET);
        }
        void AttachShm()
        {
            //将共享内存挂接到自己的地址空间当中
            _addr = shmat(_shmid,nullptr,0);//将共享内存挂接到自己的虚拟地址上。
            if((long long)_addr == -1)return;//挂接失败返回nullptr
        }
        void DetachShm()
        {
            if(_addr != nullptr)
            shmdt(_addr);
            cout<<"detach done:"<<endl;
        }
        void DeleteShm()
        {
            int n = shmctl(_shmid,IPC_RMID,nullptr);
            if(n < 0) 
            {
                cout<<"delete failed"<<endl;
                return;
            }
            cout<<"delete shm done"<<endl;
        }
        void* GetAddr()
        {
            return _addr;
        }
        void ShmMeta()
        {
    
        }
    private:
        int _shmid;
        key_t _key;
        void *_addr;
    };
    ShareMemory shm;
    

    Server.cc

    #include "ShareMemory.hpp"
    int main()
    {
        shm.CreateShm();
        shm.AttachShm();
        //接收----IPC
        char* strinfo = (char*)shm.GetAddr();//获取服务器的虚拟地址
    
        while(true)
        {
            sleep(1);
            //打印共享地址中的内容
            printf("%s\n",strinfo);
            //
        }
        shm.DetachShm();
        shm.DeleteShm();
        return 0;
    }
    
    

    Client.cc

    #include "ShareMemory.hpp"
    int main()
    {
        shm.GetShm();
        shm.AttachShm();
        //写入----IPC
        char* strinfo = (char*)shm.GetAddr();//获取客户端的虚拟地址
        char ch = 'A';
        while(ch <= 'Z')
        {
            sleep(1);
            strinfo[ch-'A'] = ch;//这里操作共享内存的时候为什么没有用系统调用?
            ch++;
        }
        shm.DetachShm();
        return 0;
    }
    

    总结

    共享内存作为一种高效的进程间通信机制,因其直接在内存中操作数据,避免了数据拷贝,提供了快速的数据传输方式。通过 shmgetshmatshmctl 等函数,Linux 系统为我们提供了灵活的共享内存操作接口。尽管共享内存具有显著的性能优势,但由于其没有内建的同步机制,使用时必须特别注意数据的一致性和进程间的同步问题。

    在实际应用中,结合信号量、消息队列等同步机制,共享内存可以为多进程间提供高效且稳定的通信手段。然而,开发者需要注意资源的管理与清理,以免造成内存泄漏或数据冲突。

    总之,共享内存是一种非常强大的进程间通信工具,但使用时需要小心谨慎,确保数据同步和资源管理得当,才能充分发挥其优势。

    作者:lyyyyrics

    物联沃分享整理
    物联沃-IOTWORD物联网 » Linux进程间通信:共享内存详解

    发表回复