Linux-System V共享内存

目录

System V共享内存

共享内存概述

共享内存是所有IPC中最快的一种。它之所以快是因为共享内存一旦映射到进程的地址空间,进程之间数据的传递就不需要涉及内核了。

管道、FIFO和消息队列,任意两个进程之间想要交换信息,都必须通过内核,内核在其中发挥了中转站的作用:

  • 发送信息的一方,通过系统调用(write或msgsnd)将信息从用户层拷贝到内核层,由内核暂存这部分信息。
  • 提取信息的一方,通过系统调用(read或msgrcv)将信息从内核层提取到应用层。

建立共享内存之后,内核完全不参与进程间的通信,这种说法严格来讲并不是正确的。因为当进程使用共享内存时,可能会发生缺页,引发缺页中断,这种情况下,内核还是会参与进来的。

允许多个进程同时操作共享内存,就不得不防范竞争条件的出现,比如有两个进程同时执行更新操作,或者一个进程在执行读取操作时,另外一个进程正在执行更新操作。因此,共享内存这种进程间通信的手段通常不会单独出现,总是和信号量、文件锁等同步的手段配合使用。

创建或打开共享内存

shmget函数负责创建或打开共享内存段,其接口定义如下:

c 复制代码
#include <sys/ipc.h>
#include <sys/shm.h>

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

第一个参数是共享内存标识,可以随机选择一个整数值,也可以使用ftok函数创建。

可以参考链接:https://blog.csdn.net/m0_51415606/article/details/138001330?spm=1001.2014.3001.5502

第二个参数size必须是正整数,表示要创建的共享内存的大小。内核以页面大小的整数倍来分配共享内存,因此,实际size会被向上取整为页面大小的整数倍。

第三个参数支持IPC_CREAT和IPC_EXCL标志位。如果没有设置IPC_CREAT标志位,那么第二个参数size对共享内存段并无实际意义,但是必须小于或等于共享内存的大小,否则会有EINVAL错误。

返回值:

创建或打开的共享内存标识。

使用共享内存

shmget函数,不过是在茫茫内存中创建了或找到了一块共享内存区域,但是这块内存和进程尚没有任何关系。要想使用该共享内存,必须先把共享内存引入进程的地址空间,这就是attach操作。

attach操作的接口定义如下:

c 复制代码
#include <sys/types.h>
#include <sys/shm.h>

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

第一个参数是调用shmget函数的返回值。

第二个参数是用来指定将共享内存放到虚拟地址空间的什么位置的。大部分的普通青年都会将第二个参数设置为NULL,表示用户并不在意,一切交由内核做主。

第三个参数是标志参数,用于指定连接共享内存的选项。

  • 如果指定了 SHM_RDONLY 标志,则进程只能以只读方式访问共享内存,否则进程可以以读写方式访问共享内存。
  • 通常可以使用 0 作为该参数,表示默认行为。

返回值:

调用成功,则返回进程虚拟地址空间内的一个地址。如果失败,就会返回(void*)-1,并且设置errno。

分离共享内存

分离操作的接口定义如下:

c 复制代码
#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void *shmaddr);

shmdt函数仅仅是使进程和共享内存脱离关系,并未删除共享内存。shmdt函数的作用是将共享内存的引用计数减1。如前所述,只有共享内存的引用计数为0时,调用shmctl函数的IPC_RMID命令才会真正地删除共享内存。

进程执行exec之后,所有attach的共享内存都会被分离。当进程终止之后,共享内存也会自动被分离。

控制共享内存

shmctl函数用来控制共享内存,函数接口定义如下:

c 复制代码
#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

当cmd为IPC_STAT和IPC_SET时,需要用到第三个参数。其中shmid_ds结构体的定义如下:

c 复制代码
struct shmid_ds {
    struct ipc_perm shm_perm;
    size_t          shm_segsz;
    time_t          shm_atime;
    time_t          shm_dtime;
    time_t          shm_ctime;
    pid_t           shm_cpid;
    pid_t           shm_lpid;
    shmatt_t       shm_nattch;
    ...
};

1、IPC_STAT

用于获取shmid对应的共享内存的信息。所谓信息,就是上面结构体的内容。

2、IPC_SET

IPC_SET也只能修改shm_perm中的uid、gid及mode。

代码示例

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>

#define SHM_SIZE 1024

int main() {
    int shmid;
    key_t key;
    char* shmaddr;

    // 生成一个唯一的key
    key = ftok(".", 's');
    if (key == -1) {
        perror("ftok");
        exit(1);
    }

    // 创建共享内存段
    shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1)
    {
        perror("shmget");
        exit(1);
    }

    // 将共享内存段映射到进程的地址空间
    shmaddr = (char*)shmat(shmid, NULL, 0);
    if (shmaddr == (char*)-1)
    {
        perror("shmat");
        exit(1);
    }

    // 创建五个子进程
    for (int i = 0; i < 5; ++i)
    {
        pid_t pid = fork();
        if (pid < 0)
        {
            perror("fork");
            exit(1);
        }
        else if (pid == 0) {  // 子进程
            while (1)
            {
                printf("Child %d: PID=%d\n", i + 1, getpid());
                char message[50];
                sprintf(message, "Message from Child %d", i + 1);
                sprintf(shmaddr, "%s", message);
                // exit(0);
                sleep(1);
            }
        }
    }

    while (1)
    {
        // 打印从共享内存中读取的消息
        printf("Parent process reads: %s\n", shmaddr);

        sleep(1);
    }

    // 等待所有子进程退出
    for (int i = 0; i < 5; ++i)
        wait(NULL);

    // 分离共享内存
    if (shmdt(shmaddr) == -1)
    {
        perror("shmdt");
        exit(1);
    }

    // 删除共享内存段
    if (shmctl(shmid, IPC_RMID, NULL) == -1)
    {
        perror("shmctl");
        exit(1);
    }

    return 0;
}
相关推荐
JunLan~3 小时前
Rocky Linux 系统安装/部署 Docker
linux·docker·容器
方竞4 小时前
Linux空口抓包方法
linux·空口抓包
sun0077005 小时前
ubuntu dpkg 删除安装包
运维·服务器·ubuntu
海岛日记5 小时前
centos一键卸载docker脚本
linux·docker·centos
oi775 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
AttackingLin5 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python
吃肉不能购6 小时前
Label-studio-ml-backend 和YOLOV8 YOLO11自动化标注,目标检测,实例分割,图像分类,关键点估计,视频跟踪
运维·yolo·自动化
学Linux的语莫6 小时前
Ansible使用简介和基础使用
linux·运维·服务器·nginx·云计算·ansible