Linux 进程间通信 之 共享内存

进程间通信(Inter Process Communication,IPC)根据通信需求不同提供了不同方式:管道共享内存消息队列信号量

本篇博客主要是介绍进程间通信的 共享内存 部分内容!

共享内存

用于进程间的数据共享。

通信原理:开辟一块物理内存空间,各个进程将同一块物理空间映射到自己的虚拟地址空间中,通过虚拟地址进行访问,进而实现数据共享。
共享内存是最快的进程间通信方式,因为通过虚拟地址空间映射后,直接通过虚拟地址访问物理内存,相较于其他方式少了两部数据拷贝操作。

操作流程:

1、创建或打开共享内存

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

int shmget(key_t key, size_t size, int shmflg);
参数:
    key: 标识符 -- 通过相同的标识符,多个进程可以打开同一个共享内存(内存空间)
	size: 要创建的共享内存大小
	shmflg: 打开方式 + 权限; IPC_CREAT | IPC_EXCL | 0664 -- 文件未存在则创建,存在则报错
	返回值:成功返回非负整数 ,即 操作句柄;失败返回 -1

2、与进程建立映射关系

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

void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
参数:
	shmid: shmget返回的操作句柄
	shmaddr: 映射首地址,通常置NULL
	shmflg: SHM_RDONLY - 只读;0 - 可读可写
	返回值:成功返回映射后的首地址 ; 失败返回(void*)-1  (-1是负1,不是减1)

3、对共享内存进行内存操作
从首地址开始,覆盖式写入
如:memcpy,strcpy,printf...

4、与进程间解除映射关系

int shmdt(const void *shmaddr);
参数:
	shmaddr: shmat 返回的映射首地址
	返回值:成功返回0,失败返回 -1

5、删除共享内存

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

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
	shmid: shmget返回的操作句柄
	cmd:要对共享内存进行的操作
	IPC_RMID:标记要删除的共享内存,
			 映射连接数为0时,删除共享内存;禁止新的映射连接产生
	buf: 用于获取或设置共享内存属性的,简单使用置NULL即可
	返回值:成功返回0,失败返回-1

注:类似函数或指令:

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id); 
用于产生一个 IPC key 标识符
解释:ftok  -  convert a pathname and a project identifier to a System V IPC key,即此函数可以直接以在程序一开始定义 IPC key 进行代替
system V & POSIX : system V -- 类 unix 的接口标准,POSIX -- 可跨平台运行的接口标准

查看 IPC:

ipcs:查看系统内核间所有进程间通信资源,输入 ipcs 所打印的内容即为以下三种指令的全部内容
aipcs -m: 查看共享内存
bipcs -q:查看消息队列
cipcs -s: 查看信号量

出现内容名称解释:
	owner:所有者         perms:操作权限         nattch:映射连接数

删除共享内存:

ipcrm -m xxxx : 删除指定的共享内存,xxxx 代表共享内存的 shmid,即操作句柄。如图,共享内存 0x01234567 的操作句柄为3,当运行 ipcrm -m 3 ,则该共享内存会被删除!
在这里插入图片描述

例程

(1)创建共享内存

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

#define IPC_KEY 0x01234567
int main()
{
    //shmget(标识符,大小,打开方式和权限) 大小为32字节,映射连接数为0
    int shmid = shmget(IPC_KEY,32,IPC_CREAT|0664);
    if(shmid < 0)
    {
        perror("shmget error");
        return -1;  
    }
    return 0;
}

运行结果如图:
在这里插入图片描述
在图中,shm_creat.c 为创建共享内存的代码,且其执行文件为 shmcreat 。而上图所展示的是,执行 shmcreat 前后的共享内存的变化!

(2)共享内存的读取

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

#define IPC_KEY 0x01234567  //定义标识符
int main()
{
    //1、创建或打开共享内存。shmget(标识符,大小,打开方式和权限)  成功返回共享内存的操作句柄
    int shmid = shmget(IPC_KEY,32,IPC_CREAT|0664);
    if(shmid < 0)
    {
        perror("shmget error");
        return -1;
    }
    // 2、建立映射关系。shmat(句柄,映射首地址,访问方式)  成功返回映射后的首地址
    void *shm_start = shmat(shmid,NULL,0);
    if(shm_start == (void*)-1)  //建立映射关系失败
    {
        perror("shmat error");
        return -1; 
    }
    //访问共享内存
    while(1)
    {
        printf("%s\n",(char *)shm_start);
        sleep(1);// 1秒钟打印1次
    }
    //解除映射关系,shmdt(映射首地址)
    int ret = shmdt(shm_start);
    if(ret < 0)
    {
        perror("shmdt error");
        return -1;
    }
    //删除共享内存 ,shmctl(句柄,操作类型,信息结构)
    shmctl(shmid,IPC_RMID,NULL);
    return 0;
}

(3)共享内存的写入

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

#define IPC_KEY 0x01234567
int main()
{
    //创建或打开共享内存
    int shmid = shmget(IPC_KEY,32,IPC_CREAT | 0664);
    if(shmid < 0)
    {
        perror("shmget error");
        return -1;
    }
    //建立映射关系
    void *shm_start = shmat(shmid,NULL,0); 
    if(shm_start == (void*)-1)  //建立映射关系失败
    {
        perror("shmat error");
        return -1;
    }
    int i=0;
    //对共享内存输入内容
    while(1)
    {
		sprintf(shm_start,"写入次数 + %d",i);
        sleep(1);
        i++;
    }
    //解除映射
    shmdt(shm_start);
    //删除共享内存
    shmctl(shmid,IPC_RMID,NULL);

    return 0;
}

运行结果:
下图中,左边实现的为共享内存的读取,右边实现的是共享内存的写入。
读取程序中设置的是1秒读一次,而写入程序设置的是1秒写入一次,所以下图中左边的 0~12 便是12秒内读取的数据。而当关闭写入的程序,因共享内存中最后的数据为 ”写入数据 + 12 “,所以在读取数据的部分会一直显示共享内存中最后写入的数据!
在这里插入图片描述
最后再附一张总结图
在这里插入图片描述
(侵权删~)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值