Linux 共享内存

system V****共享内存

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

共享内存示意图

共享内存数据结构

cpp 复制代码
struct shmid_ds {
 struct ipc_perm shm_perm; /* operation perms */
 int shm_segsz; /* size of segment (bytes) */
 __kernel_time_t shm_atime; /* last attach time */
 __kernel_time_t shm_dtime; /* last detach time */
 __kernel_time_t shm_ctime; /* last change time */
 __kernel_ipc_pid_t shm_cpid; /* pid of creator */
 __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
 unsigned short shm_nattch; /* no. of current attaches */
 unsigned short shm_unused; /* compatibility */
 void *shm_unused2; /* ditto - used by DIPC */
 void *shm_unused3; /* unused */
};

共享内存函数

shmget函数
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key: 这个共享内存段名字
size: 共享内存大小
shmflg: 由九个权限标志构成,它们的用法和创建文件时使用的 mode 模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回 -1
shmat函数
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr: 指定连接的地址
shmflg: 它的两个可能取值是 SHM_RND 和 SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回 -1

  • shmaddr为NULL,核心自动选择一个地址
  • 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 所返回的指针
返回值:成功返回 0 ;失败返回 -1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl函数
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid: 由 shmget 返回的共享内存标识码
cmd: 将要采取的动作(有三个可取值)
buf: 指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回 0 ;失败返回 -1

cmd

共享内存实测代码

cpp 复制代码
// comm.h
#ifndef COMM_H #define COMM_H
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
int createShm(int size);
int destroyShm(int shmid);
int getShm(int size);
#endif
cpp 复制代码
// comm.c
#include "comm.h"
static int commShm(int size, int flags)
{
    // 通过ftok函数,生成对应 PATHNAME 和 PROJ_ID 的键值 _key
    key_t _key = ftok(PATHNAME, PROJ_ID);
    if (_key < 0)
    {
        perror("ftok");
        return -1;
    }
    
    // 获取 _key 对应的共享内存段的描述符,内存段大小为 size 字节
    int shmid = 0;
    if ((shmid = shmget(_key, size, flags)) < 0)
    {
        perror("shmget");
        return -2;
    }
    return shmid;
}

int destroyShm(int shmid)
{
    // 通过 IPC_RMID 删除共享内存段
    if (shmctl(shmid, IPC_RMID, NULL) < 0)
    {
        perror("shmctl");
        return -1;
    }
    return 0;
}

int createShm(int size)
{
    // IPC_CREAT: 如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则直接返回共享存储标识符。
    // IPC_CREAT | IPC_EXCL: 如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则产生错误。
    // 0666: 设置权限为0666所有用户可读可写,具体查询linux权限相关内容

    return commShm(size, IPC_CREAT | IPC_EXCL | 0666);
}

int getShm(int size)
{
    return commShm(size, IPC_CREAT);
}
cpp 复制代码
// server.c
#include "comm.h"
int main()
{
    // 获取大小为4096的新的共享内存段
    int shmid = createShm(4096);

    // 将共享内存段连接到进程地址空间
    char *addr = shmat(shmid, NULL, 0);
    sleep(2);

    // 通信主体逻辑
    int i = 0;
    while (i++ < 26)
    {
        printf("client# %s\n", addr);
        sleep(1);
    }

    // 通信完成后,将共享内存段与当前进程脱离
    shmdt(addr);
    sleep(2);

    // 销毁生成的共享内存段
    destroyShm(shmid);
    return 0;
}
cpp 复制代码
// client.c
#include "comm.h"
int main()
{
    // 获取此前执行 server.c 后生成的共享内存段
    int shmid = getShm(4096);
    sleep(1);

    // 将 server.c 生成的共享内存段连接到进程地址空间
    char *addr = shmat(shmid, NULL, 0);
    sleep(2);

    // 通信主体逻辑:往共享内存段写入数据,让 server.c 接收并返回输出
    int i = 0;
    while (i < 26)
    {
        addr[i] = 'A' + i;
        i++;
        addr[i] = 0;
        sleep(1);
    }

    // 完成通信后,将共享内存段与当前进程脱离
    shmdt(addr);
    sleep(2);
    return 0;
}

system V****信号量

信号量主要用于同步和互斥的,在操作系统中,使用System V共享内存时,共享内存本身并不提供同步和互斥的机制。这意味着多个进程可以同时访问同一块共享内存空间,如果进程对内存的访问没有同步和互斥的控制,这可能会引起一些进程的资源竞争,数据损坏的问题。

进程互斥

  • 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。
  • 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
  • 在进程中涉及到互斥资源的程序段叫临界区。
  • 特性方面
    • IPC资源必须删除(最好通过进程删除),否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核。
相关推荐
cui_ruicheng11 分钟前
Linux库制作与使用(二):ELF文件与链接过程
linux·运维·服务器
ReaF_star14 分钟前
【环保】CentOS 7 安装 MySQL 8 实战记录:从安装到排障一次讲清
linux·mysql·centos
怀旧,27 分钟前
【Linux系统编程】18. Linux进程信号(上)
linux·运维·服务器
minji...43 分钟前
Linux 线程同步与互斥(五) 日志,线程池
linux·运维·服务器·开发语言·c++·算法
华清远见IT开放实验室1 小时前
嵌入式系统化课程 学习内容与服务说明
linux·stm32·学习·嵌入式·全栈·虚拟仿真·测评中心
圆山猫1 小时前
[Linux] Ubuntu 26.04 换阿里云镜像源(最新方法)
linux·ubuntu·阿里云
网络小白不怕黑2 小时前
1.2 VMware部署Rocky Linux 9 (MBR分区表,图形化安装)
linux·运维·服务器
贾斯汀玛尔斯2 小时前
每天学一个算法--Aho–Corasick 自动机
java·linux·算法
xlq223222 小时前
46.线程池
linux·开发语言
狂奔蜗牛飙车2 小时前
大数据赛项(中职组)-VMware+CentOS 7环境安装
linux·运维·centos·大数据应用与服务·大数据入门指南·中职组大数据应用及服务赛项·vmware中装centos7