Linux进程通信之共享内存

  • Post author:
  • Post category:linux


今天我为大家讲的是进程间通信的第二种方法——共享内存。首先我们得理解一下什么是共享内存以及共享内存通信的原理。

共享内存其实就是os在内存上面开辟的一段空间,让不同的进程通过虚拟地址进程空间一起看到这同一块内存,以下是共享内存原理示意图:

创建一个共享内存:


参数key

是内核层标定共享内存唯一性的,因为os创建了许多共享内存,为了能让不同的进程找到同一个共享内存,就用key来标识。因为有很多的共享内存需要被管理,所以os也是采用了先描述后组织的方法,通过数据结构(数组)把共享内存管理起来。所以该函数的

返回值

就是用户层标识共享内存的,相当于文件描述符。

参数size

创建共享内存的大小。

参数shmflg

是创建模式:

IPC_CREAT:如果共享内存不存在就创建,存在就获取共享内存。

IPC_EXCL:1.它不能够被单独使用。2.如果不存在,创建之。如果不存在,就返回报错。

所以IPC_EXCL能够保证创建出来的共享内存是

新创建

的!!

该函数的返回值:

创建一个参数key:

key是为了标识共享内存唯一性的,所以在调用shmget系统调用的时候,需要将key传进去。其实可以告诉大家的是共享内存的内核数据结构里面就存有了可key,所以说key相当于共享内存的一种属性。以下是创建key的函数:

它的返回值:

共享内存创建好了并且两个进程都找到了同一块时,就需要将共享内存的起始地址写到进程的虚拟地址空间里面。

进程与共享内存的挂接:

它的返回值:


如果成功了,返回的是共享内存的起始地址,如果失败了返回-1.

取消挂接:

int shmdt(const void *shmaddr);

接口很简单,只需要将共享内存的起始地址填入就行。

查看共享内存接口:

ipcs -m

删除共享内存接口:

ipcrm -m + shmid

shmct函数 :


功能:用于控制共享内存

原型

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

参数

shmid:





shmget


返回的共享内存标识码

cmd:


将要采取的动作(有三个可取值)

buf:


指向一个保存着共享内存的模式状态和访问权限的数据结构

返回值:成功返回


0


;失败返回


-1

了解完了上面所有函数接口,现在我们实现一个用共享内存实现进程间通信的代码:

com.hpp:

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

using namespace std;

#define PATH_NAME "/tmp"
#define PROJ_ID 0X123
#define MAX_SIZE 4096
key_t getkey()
{
    key_t key =ftok(PATH_NAME,PROJ_ID);
    if(key ==-1)
    {
        cerr<<"getkey:"<<errno<<strerror(errno)<<endl;
        exit(3);
    }
    return key;

}

int getShmHelper(key_t key, int flags)
{
    int shmid =shmget(key,MAX_SIZE,flags);
    if(shmid ==-1)
    {
        cerr<<"shmget:"<<errno<<strerror(errno)<<endl;
        exit(-1);
    }
    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* AttachShm(int shmid)
{
    void* mem =shmat(shmid,nullptr,0);
    if((long long)mem ==-1)
    {
        cerr<<"shmat:"<<errno<<strerror(errno)<<endl;
        exit(-1);

    }
    return mem;
}

void DetachShm(void* start)
{
    if(shmdt(start)==-1)
    {
        cerr<<"shmdt:"<<errno<<strerror(errno)<<endl;
        exit(3);
    }

}

void delShm(int shmid)
{
    if(shmctl(shmid, IPC_RMID, nullptr) == -1)
    {
        std::cerr << errno << " : " << strerror(errno) << std::endl;
    }
}

client.cc:

#include "com.hpp"

int main()
{
    //创建和服务端相同的key值
    key_t key =getkey();

    //获取共享内存
    int shmid =getShm(key);
    printf("shmid: %d\n", shmid);

    //将客户端和进程挂接起来
    char* start =(char*)AttachShm(shmid);
    cout<<"client 挂接成功 address: "<<endl;

    //客户端写数据
    char buffer[1024]={0};
    while(true)
    {

        cout<<"####输入数据:";
        fgets(buffer,sizeof(buffer),stdin);
        if(strlen(buffer)>0)
        {
            buffer[strlen(buffer)-1]=0;
        }
        int cnt =1;
        char buffer1[1024];
        snprintf(buffer1,sizeof(buffer1),"我是客户端,我正在给你发消息[%d][%d]:%s",getpid(),cnt++,buffer);
        memcpy(start,buffer1,strlen(buffer1));
    }

    //客户端解除挂接
    DetachShm(start);
    

    return 0;

}

server.cc:

#include "com.hpp"

int main()
{
    //获取一个key
    key_t key =getkey();
    
    //创建共享内存
    int shmid =createShm(key);
    //将进程和共享内存进行挂接
    char* start =(char*)AttachShm(shmid);
    printf("server 挂接成功 ,address: %p",start);

    //服务端读取数据。
    while(true)
    {
        cout<<"client say: "<<start<<endl;
        sleep(2);
    }

    //解除挂接
    sleep(5);
    DetachShm(start);

    //删除共享内存
    sleep(3);
    delShm(shmid);
    return 0;
}

最后我们来谈一谈共享内存的

优缺点

优先:

共享内存是进程间通信最快的一种方式,大大减少了拷贝。

缺点:

不给我进行同步与互斥的操作,对数据没有做任何保护。

共享内存相关知识就全部介绍清楚了,感谢大家多多支持!!



版权声明:本文为m0_69005269原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。