共享内存shm


1.最快的IPC通信方式

  • 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

图1:用管道或者消息队列传递数据
在这里插入图片描述
图2:用共享内存传递数据
在这里插入图片描述

2.相关API

shmget

int shmget(key_t key, size_t size, int shmflg); //创建或打开

shmat:将共享内存段连接到进程地址空间

[说明1] shmget创建/打开共享内存后,必须使用shmat将该共享内存连接到进程的地址空间,以便进程使用。不适用shmat,进程无法使用该共享内存。
[说明2] 每个进程shmat同一个内存段后得到的地址不一样–它们都属于自己的共享内存地址)
函数原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
返回值
失败:返回-1;
成功:返回一个指针p(指向共享内存的第一个字节),使用p操控共享内存。
shmat使用
p=shmat(shm_id,NULL,0);//让本进程链接到共享内存会得到一个共享内存的首地址p,可以直接通过指针p去操控共享内存。
参数
shmaddr:指定连接的地址
shmflg:两个可能取值SHM_RND和SHM_RDONLY

shmaddr和shmflg的组合关系共享内存最终地址
shmaddr为NULL,不管shmflg核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记以shmaddr为连接地址
shmaddr不为NULL且shmflg设置了SHM_RND标记连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY表示连接操作用来只读共享内存

shmdt:将共享内存段与当前进程脱离

int shmdt(const void *shmaddr);
参数shmaddr: 由shmat所返回的指针

注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl:控制共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数cmd: - IPC_STAT:获取shmid_ds结构中的值 ; - IPC_SET:设置shmid_ds结构中的值 ; - IPC_RMID:删除共享内存

其中 IPC_RMID:共享内存的删除[难点]

  1. 如果共享内存被别的程序占用,连接数/引用计数不为0,则删除共享内存不会立马被删除====>删除后,会出现一个现象—共享内存key值变成了0,引用计数减1—且此时的共享内存已经不能被其他[新shmat]的进程读取了(重启一个进程重新shmat的时候由于key已经变为0,所以新开的进程是不能再继续读取共享内存的数据的);只能被在删除之前就[已经shmat]的进程读取。
  2. 只有当连接共享内存的所有进程都死掉,Linux内核才会删除共享内存

结论:是否能直接删除共享内存?
——>关键是看在删除以后有无进程链接到该块内存<==>等同于shmctl+shmdt删除之前有多少进程已经执行了shmat。


示例代码

子进程向shm中写数据;父进程从shm中读取子进程向shm写入的数据

typedef struct student{
  int age;
  char name[10];
}student;

int main()
{
  key_t key=ftok("/home/gjw/tmp",'a');
  int shm_id=shmget(key,sizeof(int)*100,0664|IPC_CREAT);

  pid_t pid=fork();

  if(pid>0)
  {

  }
  else if(pid==0)
  {
    void* ptr=shmat(shm_id,NULL,0);
    struct student* stu=(struct student*)ptr;
    stu->age=24;
    strcpy(stu->name,"guojiawei");
    shmdt(ptr);
    exit(0);
  }
  
  sleep(1);

  wait(NULL);

  void* ptr=shmat(shm_id,NULL,0);
  printf("age=%d, name=%s\n",((student*)ptr)->age,((student*)ptr)->name);

  shmdt(ptr);

  return 0;
}


共享内存模块代码的封装

头文件myipc_shm.h

#ifndef __MYIPC_SHM_H__
#define __MYIPC_SHM_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>
#include <unistd.h>
#include <memory.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>


#ifdef __cplusplus 
extern "C" {
#endif

int IPC_CreatShm(char *shmseedfile, int shmsize, int *shmhdl);
int IPC_MapShm(int shmhdl,void **mapaddr);
int IPC_UnMapShm(void *unmapaddr);
int IPC_DelShm(int shmhdl);

#ifdef __cplusplus
}
#endif
#endif

实现文件myipc_shm.c

#include "myipc_shm.h"

int shmflag = 0;
int shmkey;

/***********************************************************************
  功能描述:    创建共享内存
  参数说明:    shmname  [in]  是共享内存名,系统中唯一标志
                shmsize  [in]  是要创建的共享内存的大小;
                shmhdl   [out] 共享内存的句柄.
  返回值:      返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_CreatShm(char *shmseedfile, int shmsize, int *shmhdl)
{
    if(shmflag == 0)            //判断接口中共享内存key是否已经存在
    {
        shmkey = ftok(shmseedfile, 'c');
        if (shmkey == -1)
        {
            perror("ftok");
            return -1;
        }

        shmflag = 1;
    }

    //创建共享内存
    *shmhdl = shmget(shmkey,shmsize,IPC_CREAT|0666);
    if (*shmhdl == -1)          //创建失败
        return -2;
    return 0;

}
/***********************************************************************
  功能描述:    关联共享内存
  参数说明:    shmhdl   [in]  共享的句柄
                mapaddr [out] 共享内存首地址
  返回值:      返回0函数执行成功;非0返回错误码
************************************************************************/
int
IPC_MapShm(int  shmhdl, void  **mapaddr)
{
    void *tempptr = NULL;

    //连接共享内存
    tempptr = (void *)shmat(shmhdl,0,SHM_RND);
    if (tempptr == (void*)-1)       //共享内存连接失败
        return -1;
    *mapaddr = tempptr;         //导出共享内存首指针

    return 0;
}
/***********************************************************************
  功能描述:    取消共享内存关联
  参数说明:    unmapaddr   [in] 共享内存首地址
  返回值:      返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_UnMapShm(void *unmapaddr)
{
    int  rv;
    //取消连接共享内存 
    rv = shmdt((char *)unmapaddr);
    if (rv == -1)           //取消连接失败
        return -1;

    return 0;
}
/***********************************************************************
  功能描述:    删除共享内存
  参数说明:    shmhdl   [in]  共享的句柄
  返回值:      返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_DelShm(int shmhdl)
{
    int  rv;
    //删除共享内存
    rv = shmctl(shmhdl,IPC_RMID,NULL);
    if(rv < 0)              //删除共享内存失败
        return -1;
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值