LINUX系统编程:进程间通信之共享内存

目录

内存共享是进程之间最快的通信方式,为什么呢?

共享内存系统调用接口

1.shmget

2.ftok

3.shmat&&shmdt

4.shmctl

使用共享内存进行通信


内存共享是进程之间最快的通信方式,为什么呢?

例:管道通信,都是以文件为媒介,需要使用系统调用将数据传入给内核,而共享内存直接与内存进行交互。

进程间通信的本质就是让进程看到同一块系统提供的资源,共享内存就是让不同进程看到同一块内存,用内存作为媒介,让两个不同进程通信。

示意图

共享内存系统调用接口

1.shmget

key:就是一个关键值,让不同的进程,通过一个key值找到同一个共享内存。

size:就是开辟多大的共享内存,共享内存的开辟是以4kb为单位的,size的大小建议设置4*n,因为即使size为7,还是会开辟8个字节的内存,剩下的一个字节就浪费了。

shmlg:想要怎么创建共享内存

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

IPC_EXCL:如果共享内存存在,就出错返回,单独使用没有意义,要配合IPC_CREAT。

返回值:成功共享内存的标识符被返回,失败-1被返回,错误码被设置去指示错误。

2.ftok

ftok使用参数pathname和最少8个有效位的参数proj_id(必须不为0)生成一个key_t类型的System V IPC密钥,适用于msgget,semget,shmget这三个函数。

pathname:指定一个路径

proj_id:8个有效位的参数

成功生成key_t类型的密钥被返回,失败-1被返回,错误码被设置。

3.shmat&&shmdt

创建共享内存之后,需要shmat挂接到进程中,本质就是,建立页表的映射,映射到共享区进程就可以使用了。

使用完需要用shmdt取消挂接参数shmaddr是shmat的返回值。

shmid:是共享内存的唯一标识符。

shmaddr:是链接共享区的地址,不设置自动连接。

shmflg:

返回值

成功返回共享内存的地址,失败返回-1,错误码被设置。

4.shmctl

控制共享内存

shmid:shmget的返回值,共享内存的唯一标识。

cmd:控制共享内存的命令

          IPC_STAR:将shmid_ds结构中的数据设置为当前共享内存的相关值。

          IPC_SET:在进程有足够权限下,将共享内存的值设置为shmid_ds的值。

         IPC_RMID:删除共享内存

使用共享内存进行通信

//clint.cc
#include"comm.hpp"

using namespace std;

int main()
{
    key_t key = getkey();//生成密钥
    cout<<"create key"<<endl;

    int shmid = getShm(key);//创建共享内存
    cout<<"get shm"<<endl;
    
    char * adress = (char*)shmatch(shmid);//挂接共享内存
    cout<<"shm attch"<<endl;

    memset(adress,0,8);

    int fd = sny.writeordie();//以写方式打开管道文件
    for(char c = 'A'; c < 'Z'; c++)//向文件中写入
    {
        adress[c-'A'] = c;
        sny.Wakeup(fd);//唤醒共享内存
        sleep(1);
    }


    shmdeatch(adress);//取消挂接
    cout<<"shm deatch"<<endl;
    return 0;
}
//server.cc

#include"comm.hpp"
#define size 8 //共享内存大小为8个字节

using namespace std;
int main()
{
    
    key_t key = getkey();//获取密钥
    cout<<"create key"<<endl;

    cout<<"key:"<<key<<endl;
    
    int shmid = createShm(key,size);//创建共享内存
    cout<<"create shm"<<endl;

    cout<<"shmid:"<<shmid<<endl;
    char * adress = (char*)shmatch(shmid);//挂接共享内存
    cout<<"shm attch"<<endl;

    int fd = sny.readordie();//以读方式打开管道

    while(true)//向共享内存中写入
    {
      if(!sny.Wait(fd))//共享内存等待失败就退出
      {
         cout<<"read over"<<endl;
         break;
      }
        cout<<"content:"<<adress<<endl;
    }

    shmdeatch(adress);//取消挂接
    cout<<"shm deatch"<<endl;

    shmdel(shmid);
    cout<<"shm del"<<endl;//当共享内存挂接数为0是就删除

    return 0;
}
//comm.hpp

#pragma once

#include<iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include<unistd.h>
#include"pipe.hpp"
#define paths "/home/wys"
#define proj 0x66

enum
{
    ftok_fail = -1,
    createShm_fail = -2,
    getshm_fail = -3,
    shmat_fail = -4,
    shmdel_fail = -5,
};


fifo sny;//创建一个管道,让共享内存有自动协同机制

key_t getkey()
{
    key_t ret = ftok(paths,proj);
    if(ret == -1)
    {
        std::cerr<<"ftok fail:"<<strerror(errno)<<std::endl;
        return ftok_fail;
    }
    return ret;
}

int createShm(key_t key,size_t size)
{
    int shm_id = shmget(key,size,IPC_CREAT|IPC_EXCL|0666);
    if(shm_id == -1)
    {
        std::cerr<<"shmget fail:"<<strerror(errno)<<std::endl;
        return createShm_fail;
    }
    return shm_id;
}

int getShm(key_t key)
{
    int shm_id = shmget(key,0,IPC_CREAT);
    if(shm_id == -1)
    {
        std::cerr<<"shmget fail:"<<strerror(errno)<<std::endl;
        return getshm_fail;
    }
    return shm_id;
}

void * shmatch(int shmid)
{
    void *adress = shmat(shmid,nullptr,0);
    if((long long int) adress == -1)
    {
        std::cerr<<"shmat fail:"<<strerror(errno)<<std::endl;
        return nullptr;
    }
    return adress;
}

void shmdeatch(void * adress)
{
    int ret = shmdt(adress);
    if(ret == -1)
    {
        std::cerr<<"shmdeatch fail:"<<strerror(errno)<<std::endl;
    }
}

void shmdel(int shmid)
{
    int ret = shmctl(shmid,IPC_RMID,nullptr);
    if(ret == -1)
    {
        std::cerr<<"shmdel fail:"<<strerror(errno)<<std::endl;
    }
    else
    {
        std::cout<<"delete success"<<std::endl;
    }
}
#pragma once
#include <iostream>
#include <string>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>
using namespace std;
#define path "./fifo"
#define mode 0666
class fifo
{
public:
    fifo()
    {
        umask(0);
        int ret = mkfifo(_path.c_str(), mode);
        if (ret == 0)
        {
            cout << "mkfifo sucess" << endl;
        }
        else if (ret == -1)
        {
            cout << "mkfifo fail,errno:" << errno << "errstirng:" << strerror(errno) << endl;
        }
    }

    int writeordie()
    {
        int fd = open(path, O_WRONLY);
        if (fd == -1)
        {
            std::cout << "openwrite fail:" << strerror(errno) << endl;
        }
        return fd;
    }

    int readordie()
    {
        int fd = open(path, O_RDONLY);
        if (fd == -1)
        {
            std::cout << "openread fail:" << strerror(errno) << endl;
        }
        return fd;
    }

    bool Wait(int fd)
    {
        bool ret = true;
        char c = 0;
        int n = read(fd, &c, sizeof(c));
        if (n == sizeof(c))
        {
            std::cout << "wait scuess" << std::endl;
        }
        else
        {
            ret = false;
        }
        return ret;
    }

    void Wakeup(int fd)
    {
        char c = 0;
        int n = write(fd, &c, sizeof(c));
        if (n != sizeof(c))
        {
            std::cout << "wake up fail" << endl;
        }
        std::cout << "wake up success" << endl;
    }

    ~fifo()
    {
        int ret = unlink(_path.c_str());
        if (ret == 0)
        {
            cout << "unlink sucess" << endl;
        }
        else if (ret == -1)
        {
            cout << "unlink fail,errno:" << errno << "errstirng:" << strerror(errno) << endl;
        }
    }

private:
    string _path = path;
};

  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值