Linux进程通信之共享内存

时间:2022-02-13 01:12:01

共享内存

进程间共享内存通信,原理很简单,系统开辟一块内存空间,将其映射到物理内存,不同进程可以通过访问这个公共的共享空间完成数据交换,达到进程间通信的目的。访问快,使用简单。不需要额外的函数(如read write等)

使用共享内存包含两个步骤:

  • 创建共享内存,使用shmget函数。
  • 映射共享内存,将这段创建的共享内存映射到具体的进程空间,使用shmat函数。

shmget函数结构如下:

    int shmget(key_t key,int size,int shmflg)  

参数说明:
key:

  • 0(IPC_PRIVATE):会建立新共享内存对象;
  • 大于032位整数:视参数shmflg来确定操作。通常要求此值来源于ftok返回的IPC键值

size:

  • 大于0的整数:新建的共享内存大小,以字节为单位;
  • 0:只获取共享内存时指定为0

shmflag:

  • 0:取共享内存标识符,若不存在则函数会报错
  • IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
  • IPC_CREAT|IPC_EXCL:如果内核中不存在键值 与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错
  • shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

返回值:

  • 如果成功,返回共享内存标识符;
  • 如果失败返回-1

shmat()函数结构如下:

    int shmat(int shmid,char *shmaddr,int flag)  

参数说明

shmid:

  • shmget函数返回的共享存储标识符;

shmaddr

  • 指定映射内存地址指针(通常写0,由系统自动分配共享内存的物理地址,防止自己指定的地址被占用而导致创建失败);

flag

  • 决定以什么方式来确定映射地址(通常为0);

返回值:

  • 如果成功返回共享内存映射到进程中的地址;如果失败,返回-1;

当一个进程不在需要共享内存时,使用shmdt函数把它从进程地址空间中脱离

shmdt()函数结构如下:

int shmdt(char *shmaddr)  

参数说明:

shmaddr:

  • 共享内存的物理地址 由shmat函数返回

共享内存控制函数shmctl函数,用于控制共享内存

intshmctl(intshmid, intcmd, structshmid_ds *buf);

参数:

shmid:

  • shmget返回的共享内存标识码

cmd:

  • 将要采取的动作(有三个可取值)
    • IPC_STATshmid_ds结构中的数据设置为共享内存的当前关联值
    • IPC_SET 在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中的值
    • IPC_RMID 删除共享内存段

buf:

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

返回值:

  • 成功返回0;失败返回-1

创建共享内存的代码:

#include<stdio.h>
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>

typedef struct _Teacher
{
    char name[64];
    int age;
}Teacher;

int main(int argc, char *argv[])
{
    int ret = 0;
    int    shmid;
    //创建共享内存 ,相当于打开文件,文件不存在则创建
    shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT | 0666); 
    if (shmid == -1)
    {
        perror("shmget err");
        return errno;
    }
    printf("shmid:%d \n", shmid);
    Teacher *p = NULL;
    //将共享内存段连接到进程地址空间
    p = shmat(shmid, NULL, 0);//第二个参数shmaddr为NULL,核心自动选择一个地址
    if (p == (void *)-1 )
    {
        perror("shmget err");
        return errno;
    }
    strcpy(p->name, "aaaa");
    p->age = 33;
    //将共享内存段与当前进程脱离
    shmdt(p);

    printf("键入1 删除共享内存,其他不删除\n");

    int num;
    scanf("%d", &num);
    if (num == 1)
    {
        //用于控制共享内存
        ret = shmctl(shmid, IPC_RMID, NULL);//IPC_RMID为删除内存段
        if (ret < 0)
        {
            perror("rmerrr\n");
        }
    }                 

    return 0;    
}

获取共享内存的代码:

#include<stdio.h>
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>

typedef struct _Teacher
{
    char name[64];
    int age;
}Teacher;

int main(int argc, char *argv[])
{
    int ret = 0;
    int    shmid;
    //shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT |IPC_EXCL | 0666); 
    //打开获取共享内存
    shmid = shmget(0x2234, 0, 0); 
    if (shmid == -1)
    {
        perror("shmget err");
        return errno;
    }
    printf("shmid:%d \n", shmid);
    Teacher *p = NULL;
    //将共享内存段连接到进程地址空间
    p = shmat(shmid, NULL, 0);
    if (p == (void *)-1 )
    {
        perror("shmget err");
        return errno;
    }

    printf("name:%s\n", p->name);
    printf("age:%d \n", p->age);
    //将共享内存段与当前进程脱离
    shmdt(p);

    printf("键入1 程序暂停,其他退出\n");

    int num;
    scanf("%d", &num);
    if (num == 1)
    {
        pause();
    }                
    return 0;
}