五.共享内存

发布时间:2023年12月31日

目录

1 共享内存概述

特点和优势

Linux 中的共享内存

使用场景

1.1共享内存的特点

2 共享内存操作

2.1 获得一个共享存储标识符

2.2 共享内存映射(attach)

2.3 解除共享内存映射(detach)

2.4 案例:使用共享内存实现读写操作

3 共享内存的控制


1 共享内存概述

?共享内存概述: 共享内存允许两个或者多个进程共享给定的存储区域。

共享内存是一种用于实现进程间通信的机制,允许多个进程共享同一块物理内存区域。这种通信方式允许多个进程访问共享的数据,避免了数据复制和传输开销。

特点和优势

  1. 高效性:相比于其他 IPC 机制,共享内存提供了更快速的数据传输方式,因为数据直接存在于物理内存中,而不需要在进程间复制。
  2. 方便性:共享内存允许多个进程共享同一块内存,从而可以更容易地实现数据共享。
  3. 灵活性:进程可以随时读写共享内存中的数据,这种方式可以用于频繁的数据交换和通信。

Linux 中的共享内存

在 Linux 中,共享内存是 System V IPC 的一部分,可以通过一组函数来创建、连接和控制共享内存区域。主要函数包括:

  • shmget():创建或获取一个共享内存标识符。
  • shmat():将共享内存连接到进程的地址空间。
  • shmdt():从进程中分离共享内存。
  • shmctl():对共享内存进行控制操作,如删除或修改。

共享内存使用一个标识符来标识特定的共享内存区域。进程使用这个标识符来连接到共享内存区域并进行数据交换。

使用场景

共享内存适用于需要高效、频繁的数据交换和通信的场景,特别是:

  • 多个进程共享数据:多个进程需要共享一些数据,如数据库缓存、共享缓冲区等。
  • 高性能计算:在并行计算或高性能计算环境中,共享内存提供了一种高效的通信方式。
  • 进程间数据共享:用于共享大量数据,避免数据复制和通信开销。

共享内存在多种场景下提供了一种高效、便捷的进程间通信方式,但需要注意数据的同步和互斥,以避免数据一致性问题。

1.1共享内存的特点

1、 共享内存是进程间共享数据的一种最快的方法。 一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。

2、使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。 若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。

总结:共享内存是进程间通信方式中效率最高的,原因在于进程是直接在物理内存上进行操作(直接操作内存),将物理地址映射到用户进程这,所以只要对其地址进行操作,就是直接对物理地址操作。

在 ubuntu 12.04 中共享内存限制值如下

1、共享存储区的最小字节数:1

2、共享存储区的最大字节数:32M

3、共享存储区的最大个数:4096

4、每个进程最多能映射的共享存储区的个数:4096

2 共享内存操作

2.1 获得一个共享存储标识符

man shmget

? ? ? ?#include <sys/ipc.h>
? ? ? ?#include <sys/shm.h>

? ? ? ?int shmget(key_t key, size_t size, int shmflg);

?功能:创建一个共享内存

参数:

?key:键值,唯一的键值确定唯一的共享内存

?size:创建的共享内存的大小

?shmflg:共享内存的访问权限, 一般为 IPC_CREAT | 0777

?返回值:

?成功:共享内存的id

?失败:‐1

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

int main(int argc, char const *argv[])
{
    //使用ftok函数获取键值
    key_t mykey;
    if((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }

    //通过shmget函数创建或者打开一个共享内存,返回一个共享内存的标识符
    int shmid;
    if((shmid = shmget(mykey, 500, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to shmget");
        exit(1);
    }

    printf("shmid = %d\n", shmid);

    system("ipcs -m");
    
    return 0;
}

使用shell命令操作共享内存:

?查看共享内存

?ipcs ‐m

?删除共享内存

?ipcrm ‐m shmid

2.2 共享内存映射(attach)

man shmat

?????? #include <sys/types.h>
? ? ? ?#include <sys/shm.h>

? ? ? ?void *shmat(int shmid, const void *shmaddr, int shmflg);

? ? ? ?int shmdt(const void *shmaddr);

?功能:映射共享内存

?参数

shmid:共享内存的id

?shmaddr:映射的地址,设置为NULL为系统自动分配

shmflg:标志位

0:共享内存具有可读可写权限。

?SHM_RDONLY:只读。

?返回值:

?成功:映射的地址

?失败:‐1

注意: shmat函数使用的时候第二个和第三个参数一般设为NULL和0,即系统自动指定共享 内存地址,并且共享内存可读可写。

2.3 解除共享内存映射(detach)

???????#include <sys/types.h>
? ? ? ?#include <sys/shm.h>

int shmdt(const void *shmaddr);

?功能:解除共享内存的映射

参数:

shmaddr:映射的地址,shmat的返回值。

?返回值:

成功:0

失败:‐1

2.4 案例:使用共享内存实现读写操作

write.c

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

typedef struct{
    int a;
    char b;
}MSG;

int main(int argc, char const *argv[])
{
    //使用ftok函数获取键值
    key_t mykey;
    if((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }

    //通过shmget函数创建或者打开一个共享内存,返回一个共享内存的标识符
    int shmid;
    if((shmid = shmget(mykey, 500, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to shmget");
        exit(1);
    }

    system("ipcs -m");

    //使用shmat函数映射共享内存的地址
    //char *text;
    MSG *text;
    if((text = shmat(shmid, NULL, 0)) == (void *)-1)
    {
        perror("fail to shmat");
        exit(1);
    }

    //通过shmat的返回值对共享内存操作
    //strcpy(text, "hello world");
    text->a = 100;
    text->b = 'w';

    //操作完毕后要接触共享内存的映射
    if(shmdt(text) == -1)
    {
        perror("fail to shmdt");
        exit(1);
    }

    system("ipcs -m");
    
    return 0;
}

read.c

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

typedef struct{
    int a;
    char b;
}MSG;

int main(int argc, char const *argv[])
{
    //使用ftok函数获取键值
    key_t mykey;
    if((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }

    //通过shmget函数创建或者打开一个共享内存,返回一个共享内存的标识符
    int shmid;
    if((shmid = shmget(mykey, 500, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to shmget");
        exit(1);
    }

    system("ipcs -m");

    //映射共享内存的地址
    //char *text;
    MSG *text;
    if((text = shmat(shmid, NULL, 0)) == (void *)-1)
    {
        perror("fail to shmat");
        exit(1);
    }

    //获取共享内存中的数据
    //printf("text = %s\n", text);
    printf("a = %d, b = %c\n", text->a, text->b);

    //解除共享内存映射
    if(shmdt(text) == -1)
    {
        perror("fail to shmdt");
        exit(1);
    }

    system("ipcs -m");
    
    return 0;
}

3 共享内存的控制

man shmctl

? ? ? ?#include <sys/ipc.h>
? ? ? ?#include <sys/shm.h>

? ? ? ?int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:设置或者获取共享内存里的属性

?参数:

?shmid:共享内存的id

?cmd:执行操作的命令

?IPC_STAT 获取共享内存的属性

?IPC_SET 设置共享内存的属性

?IPC_RMID 删除共享内存

?shmid_ds:共享内存的属性结构体

?返回值: 成功:0 ?失败:‐1

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

int main(int argc, char const *argv[])
{
    //使用ftok函数获取键值
    key_t mykey;
    if((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }

    //通过shmget函数创建或者打开一个共享内存,返回一个共享内存的标识符
    int shmid;
    if((shmid = shmget(mykey, 500, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to shmget");
        exit(1);
    }

    printf("shmid = %d\n", shmid);

    system("ipcs -m");

    //通过shmctl函数删除共享内存
    if(shmctl(shmid, IPC_RMID, NULL) == -1)
    {
        perror("fail to shmctl");
        exit(1);
    }

    system("ipcs -m");
    
    return 0;
}

文章来源:https://blog.csdn.net/qq_45657052/article/details/135307070
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。