【Linux操作系统】进程间通信(2)——共享内存

一、共享内存

共享内存的原理:
进程A把它的数据在地址空间中通过页表映射到物理内存中,进程B通过页表获取物理内存的物理地址,得到数据。
在这里插入图片描述
共享内存在物理内存可能有多个,那么两个进程如何确定找到的是同一个共享内存呢?所以要给每个共享内存提供唯一的标识。

在这里插入图片描述
key_t key是标识共享内存唯一性的。shmflg有3种,IPC_CREAT:如果共享内存不存在,就创建,如果已经存在,就获取它;IPC_EXCL:不能单独使用,没有意义;IPC_CREAT || IPC_EXCL:如果共享内存不存在,就创建,否则出错返回。第3个与第1个的区别是只要共享内存创建成功,那么一定是新的共享内存。

key通过系统调用ftok获取
在这里插入图片描述
ftok的参数由用户提供,通信的两个进程都是同样的pathname和proj_id,这样返回的key是相同的,使用的就是同一个共享内存了。

使用共享内存,一定是一个进程创建共享内存,另一个进程获取。共享内存如果我们没有主动释放,它就会一直存在,因为共享内存是随系统的,除非重启系统,否则一直存在。而文件是随进程的。

通过指令查看共享内存:ipcs -m;通过指令删除共享内存:ipcrm -m shmid

挂接的意义:可使用add返回值,直接访问共享内存。nattch表示几个进程与共享内存相关联

共享内存不提供进程间的协同机制:当只有读端时,写端不写,读端不会发生阻塞等待。

共享内存的优缺点:优点:共享内存是所有进程间通信中最快的;缺点:不提供进程间的协同机制,因此会有数据传输不一致的问题。为什么最快?因为一个进程将数据传输到物理内存,另一个进程就能通过页表将数据返回虚拟地址,这个过程数据只拷贝了一次,而管道至少要两次。

代码:
Comm.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__

#include <iostream>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdlib.h>
#include <sys/shm.h>

using namespace std;
const char* path  = "/home/yss";
int proj_id =  0x66;
int defaultsize = 4096;

//16进制转换
string ToHex(key_t key)
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "0x%x", key);
    return buffer;
}

//获取key
key_t getkeyOrdie()
{
    key_t k = ftok(path, proj_id);
    if(k < 0)
    {
        cout << "getkey fail, error: " << errno << "stringerror: " << strerror(errno) << endl;
        exit(1);
    }
    return k;
}

//获取shm
int getShmOrdie(key_t key, size_t size, int flag)
{
    int shmid = shmget(key, size, flag);
    if(shmid < 0)
    {
        cout << "getshm fail, error: " << errno << "stringerror: " << strerror(errno) << endl;
        exit(1);
    }
    return shmid;
}

//创建shm
int CreateShm(key_t key, size_t size)
{
    return getShmOrdie(key, size, IPC_CREAT|IPC_EXCL|0666);
}

//得到shm
int GetShm(key_t key, size_t size)
{
    return getShmOrdie(key, size, IPC_CREAT);
}


//删除shm
void DeleteShm(int shmid)
{
    int n = shmctl(shmid, IPC_RMID, nullptr);
    if(n < 0)
    {
        cout << "delete fail" << endl;
    }
    else 
    {
        cout << "delete success" << endl;
    }
}

//挂接shm
void* AttachShm(int shmid)
{
    void* addr = shmat(shmid, nullptr, 0);
    if((long long int)addr == -1)
    {
        cout << "attach fail" << endl;
        return nullptr;
    }
    return addr;
}

//去挂接
void DetachShm(void* addr)
{
    int n = shmdt(addr);
    if(n < 0)
    {
        cout << "detach fail" << endl;
    }
}

#endif

Fifo.hpp

#ifndef __FIFO_FPP__
#define __FIFO_FPP__

#include <iostream>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <cstdlib>
#include <fcntl.h>
#include <cassert>

using namespace std;
#define Mode 0666
#define Path "fifo"
class Fifo
{
public:
    Fifo(string path = Path) : _path(path)
    {
        umask(0);
        //创建管道
        int n = mkfifo(_path.c_str(), Mode);
        if(n == 0)
        {
            cout << "fifo create success" <<endl;
        }
        else 
        {
            cout << "fifo fail, errno: " << errno << " stringerrno: " << strerror(errno) << endl;
        }
    }
    ~Fifo()
    {
        //删除管道,管道也是文件
        int n = unlink(_path.c_str());
        if(n == 0)
        {
            cout << "fifo remove success" <<endl;
        }
        else 
        {
            cout << "fifo remove, errno: " << errno << " stringerrno: " << strerror(errno) << endl;
        }
    }

private:
    string _path;//文件名+文件路径
};

class Sync
{
public:
    Sync() :wfd(-1), rfd(-1)
    {}

    void OpenWrite()
    {
        wfd = open(Path, O_WRONLY);
        if(wfd < 0)
        {
            cout << "writeopen fail" <<endl;
            exit(1);
        }
    }

    void OpenRead()
    {
        rfd = open(Path, O_RDONLY);
        if(rfd < 0)
        {
            cout << "readopen fail" <<endl;
            exit(1);
        }
    }

    bool Wait()
    {
        bool ret = true;
        uint32_t c = 0;
        ssize_t n = read(rfd, &c, sizeof(uint32_t));
        if(n == sizeof(uint32_t))
        {
            cout << "等待后,读取数据..." << endl;
        }
        else if(n == 0)
        {
            ret = false;
        }
        else 
        {
            return false;
        }
        return ret;
    }   

    void Wakeup()
    {
        uint32_t c = 0;
        ssize_t n = write(wfd, &c, sizeof(uint32_t));
        assert(n == sizeof(uint32_t));
        cout << "唤醒..." << endl;
    }

    ~Sync()
    {}

private:
    int wfd;
    int rfd;
};

#endif

ShmServer.cc

#include "Comm.hpp"
#include "Fifo.hpp"

int main()
{
    //获取key
    key_t key = getkeyOrdie();
    cout << "key: " << ToHex(key) << endl;
    
    //创建共享内存
    int shmid = CreateShm(key, defaultsize);
    cout << "shmid: " << shmid << endl;

    //挂接
    char* addr = (char*)AttachShm(shmid);
    cout << "attach success" << endl;

    //引入管道
    Fifo fifo;
    Sync syn;
    syn.OpenRead();

    //通信
    for(;;)
    {
        if(!syn.Wait()) break;
        cout << "addr message: " <<addr<<endl;
        sleep(1);
    }

    //去挂接
    DetachShm(addr);
    cout << "detach success" << endl;

    //删除共享内存
    DeleteShm(shmid);

    return 0;
}

ShmClient.cc

#include "Comm.hpp"
#include "Fifo.hpp"

int main()
{
    key_t key = getkeyOrdie();
    cout << "key: " << ToHex(key) << endl;

    int shmid = GetShm(key, defaultsize);
    cout << "shmid: " << shmid << endl;

    //挂接
    char* addr = (char*)AttachShm(shmid);
    cout << "attach success" << endl;

    memset(addr, 0, defaultsize);
    Sync syn;
    syn.OpenWrite();

    //通信
    for(int i='A'; i<='Z'; i++)
    {
        addr[i-'A'] = i;
        sleep(1);
        syn.Wakeup();
    }

    //去挂接
    DetachShm(addr);
    cout << "detach success" << endl;
    sleep(5);

    return 0;
}

在这里插入图片描述

  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值