Skip to content

sys/shm.h

共享内存允许两个或多个进程访问给定的同一块存储区域。

一般情况下,共享内存应与信号量一起使用,由信号量帮它实现读写操作的同步。

shmget

创建一块新的共享内存,或打开一块已经存在的共享内存

cpp
/**
 * 参数
 *   key 通常为整数,代表共享内存的键值;
 *   size 用于设置共享内存的大小;
 *   shmflg 用于设置shmget()函数的创建条件(一般设置为IPC_CREAT或IPC_EXCL)及进程对共享内存的读写权限。
 * 
 * 返回
 *   若调用成功,将会返回一个共享内存标识符(该标识符是一个非负整数);
 *   若调用失败,将会返回-1,并对errno进行设置。
 */
int shmget(key_t key, size_t size, int shmflg);

shmat

进行地址映射,将共享内存映射到进程虚拟地址空间中

cpp
/**
 * 参数
 *   shmid为共享内存标识符,该标识符一般由shmget()函数返回;
 *   shmaddr为一个指针类型的传入参数,用于指定共享内存映射到虚拟内存时的虚拟地址,当设置为NULL时,映射地址由系统决定;
 *   shmflg用于设置共享内存的使用方式,
 *     若shmflg设置为SHM_RDONLY,则共享内存将以只读的方式进行映射,当前进程只能从共享内存中读取数据。
 * 
 * 返回
 *   若调用成功,会返回映射的地址,并更改共享内存shmid_ds结构中的属性信息;
 *   若调用失败,会返回-1,并设置errno。
 */
void *shmat(int shmid, const void *shmaddr, int shmflg);

shmdt

解除物理内存与进程虚拟地址空间的映射关系

cpp
/**
 * 参数
 *   shmaddr 为shmat()函数返回的虚拟空间地址
 * 
 * 返回
 *   函数调用成功则返回0,并修改共享内存的shmid_ds结构中的属性信息;
 *   否则返回-1。
 */
int shmdt(const void *shmaddr);

shmctl

对已存在的共享内存进行操作,具体的操作由参数决定

cpp
/**
 * 参数
 *   shmid表示共享内存标识符;
 *   cmd表示要执行的操作,常用的设置为IPC_RMID,功能为删除共享内存;
 *   buf用于对共享内存的管理信息进行设置,该参数是一个结构体指针
 * 
 * 返回
 *   调用成功则返回0,
 *   否则返回-1,并设置errno。
 */
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid_ds 是一个为了方便对共享内存进行管理,由内核维护的存储共享内存属性信息的结构体,该结构体的类型定义如下:

cpp
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;            //创建共享内存进程的id
    pid_t           shm_lpid;            //最近操作共享内存进程的id
    shmatt_t        shm_nattch;        //与共享内存发生映射的进程数量
    ...
};

示例

示例:创建两个进程,使用共享内存机制实现这两个进程间的通信。

shm_w.c

cpp
#include <stdio.h>
#include <sys/ipc.h> // ftok
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#define SEGSIZE 4096 // 定义共享内存容量

// 读写数据结构体
typedef struct
{
    char name[8];
    int age;
} Student;

int main()
{
    int shm_id, i;
    key_t key;
    char name[8];
    Student *smap;
    key = ftok("/", 0); // 获取关键字
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }

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

    // 创建共享内存
    shm_id = shmget(key, SEGSIZE, IPC_CREAT | IPC_EXCL | 0664);
    if (shm_id == -1)
    {
        perror("create shared memory error\n");
        return -1;
    }
    printf("shm_id=%d\n", shm_id);

    smap = (Student *)shmat(shm_id, NULL, 0); // 将进程与共享内存绑定
    memset(name, 0x00, sizeof(name));
    strcpy(name, "Jhon");
    name[4] = '0';
    for (i = 0; i < 3; i++) // 写数据
    {
        name[4] += 1;
        strncpy((smap + i)->name, name, 5);
        (smap + i)->age = 20 + i;
    }

    if (shmdt(smap) == -1) // 解除绑定
    {
        perror("detach error");
        return -1;
    }
    return 0;
}

shm_r.c

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

typedef struct
{
    char name[8];
    int age;
} Student;

int main()
{
    int shm_id, i;
    key_t key;
    Student *smap;

    // 获取关键字
    key = ftok("/", 0);
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    printf("key=%d\n", key);

    // 创建共享内存
    shm_id = shmget(key, 0, 0);
    if (shm_id == -1)
    {
        perror("shmget error");
        return -1;
    }
    printf("shm_id=%d\n", shm_id);

    // 将进程与共享内存绑定
    smap = (Student *)shmat(shm_id, NULL, 0);

    // 读数据
    for (i = 0; i < 3; i++)
    {
        printf("name:%s\n", (*(smap + i)).name);
        printf("age :%d\n", (*(smap + i)).age);
    }

    // 解除绑定
    if (shmdt(smap) == -1)
    {
        perror("detach error");
        return -1;
    }

    // 删除共享内存
    shmctl(shm_id, IPC_RMID, NULL);

    return 0;
}

在终端中运行可执行程序,先执行shm_w创建共享内存,并向共享内存中写入数据;之后使用shm_r从共享内存中读取数据,数据读取完毕之后将共享内存删除。程序执行后终端打印的信息分别如下。

shell
# 写入
gcc shm_w.c -o shm_w && ./shm_w
key=262146
shm_id=65537

# 读取
% gcc shm_r.c -o shm_r && ./shm_r
key=262146
shm_id=65537
name:Jhon1
age :20
name:Jhon2
age :21
name:Jhon3
age :22

之后再次执行程序shm_r,终端打印的信息如下:

shell
% gcc shm_r.c -o shm_r && ./shm_r
key=262146
shmget error: No such file or directory

由打印结果可知,共享内存区域已在程序shm_r执行结束前被删除。