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;
}
相关推荐
粤海科技君6 分钟前
如何使用腾讯云GPU云服务器自建一个简单的类似ChatGPT、Kimi的会话机器人
服务器·chatgpt·机器人·腾讯云
傲骄鹿先生15 分钟前
阿里云centos7.9服务器磁盘挂载,切换服务路径
服务器·阿里云·磁盘
有谁看见我的剑了?44 分钟前
Ubuntu 22.04.5 配置vlan子接口和网桥
服务器·网络·ubuntu
铁锤妹妹头发多1 小时前
新手用docker真**难受
运维·docker·容器
2739920291 小时前
Ubuntu20.04 安装build-essential问题
linux
超栈1 小时前
HCIP(11)-期中综合实验(BGP、Peer、OSPF、VLAN、IP、Route-Policy)
运维·网络·网络协议·计算机网络·web安全·网络安全·信息与通信
Cachel wood1 小时前
Github配置ssh key原理及操作步骤
运维·开发语言·数据库·windows·postgresql·ssh·github
编程一生1 小时前
回调数据丢了?
运维·服务器·前端
华为云PaaS服务小智1 小时前
华为大咖说 | 浅谈智能运维技术
运维·华为·华为云
zhd15306915625ff3 小时前
化工厂主要涉及的自动化备件有哪些?
运维·自动化·化工厂