共享内存详解
共享内存
共享内存是一种允许多个进程或线程访问同一块内存区域的机制,它是最快的可用进程间通信(IPC)形式之一。
共享内存的特点
- 快速性:由于多个进程或线程直接访问同一块内存区域,因此数据交换和通信的速度非常快,远超过其他IPC机制(如命名管道、消息队列等)。
- 高效性:共享内存减少了数据的冗余拷贝,降低了内存消耗,提高了系统的整体效率。
- 灵活性:共享内存可以与其他通信机制(如信号量)结合使用,以实现进程间的同步和互斥,从而满足复杂的通信需求。
shmget函数
功能:用于创建贡献内存(如果创建成功,一定是一个新的共享内存)
参数:
- key:这个共享内存的名字(共享内存在内核中的唯一性标识)
- size:这个共享内存大小
- shmflg:有九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样
IPC_CREAT:如果存在,直接获取它。如果不存在,就创建它
IPC_CREAT | IPC_EXCL 如果不存在创建它。如果存在,出错返回
返回值: 成功返回一个非负整数,即该共享内存的标识码,失败返回-1。
ftok函数
功能:ftok函数是一个在Unix和Linux系统中广泛使用的函数,用于生成一个唯一的键(key),这个键通常用于IPC(进程间通信)机制,如消息队列、信号量或共享内存。ftok的名字来源于“file to key”的缩写,意味着它可以根据文件生成一个键。
参数:
- pathname:指向文件路径的指针,这个文件通常是项目中的一个已知文件。这个路径不需要指向一个实际存在的文件,但必须是唯一的,以便在不同的项目或实例中生成不同的键。
- proj_id:一个8位(1字节)的整数,通常用于进一步区分同一路径下的不同键。
返回值:如果成功,ftok函数返回一个唯一的键(key_t类型)。如果失败,返回-1,并设置errno以指示错误。
shmat函数
功能:将共享内存连接到进程地址空间中
参数:
- shmid:共享内存的标识
- shamaddr:指定连接的地址
- shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
shmaddr为NULL,内核自动选择一个地址
shmaddr不为NULL而且shmflg无SHM_RND标识,则以shmaddr为连接地址
shmaddr不为NULL而且shmflg设置SHM_RND标识,则连接地址会自动向下调整为SHMLBA的整数倍。(shmaddr-(shmaddr%SHMLBA))
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
返回值:成功返回一个指针,指向共享内存的第一节,失败返回-1。
shmdt函数
功能:将共享内存和当前线程脱离
参数:
- shmaddr:之前调用shmat返回的指针
返回值:成功返回0,失败返回-1
一个进程打开文件,进程退出时,这个被打开的文件就会被系统自动释放掉(文件的生命周期随进程)
共享内存被创建,如果进程结束,我们没有主动释放,则内存会一直存在,除非重启系统(共享内存生命周期随内核)
shmctl函数
功能:用于控制共享内存
参数:
- shmid:由shmget返回的共享内存标识码
- cmd:将要采取的动作(有三个取值)
- buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0,失败返回-1
代码实操
comm.cc#pragma once#include <stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include <unistd.h>#define PATHNAME "."
#define PROJ_ID 0x6666
client.cc
#include"comm.h"int main()
{key_t key = ftok(PATHNAME,PROJ_ID);if(key<0){perror("ftok error");return(-1);}int shmid = shmget(key,4096,IPC_CREAT|IPC_EXCL|0666);if(shmid<0){perror("shmget error");return(-2);}char* addr = (char*)shmat(shmid,nullptr,0);sleep(1);for(int i=0;i<26;i++){addr[i] = 'A' + i;addr[i+1] = '\0';sleep(1);}shmdt(addr);int n = shmctl(shmid,IPC_RMID,NULL);if(n<0){perror("shmctl error");return(-3);}return 0;
}
server.cc
#include"comm.h"int main()
{key_t key = ftok(PATHNAME,PROJ_ID);if(key<0){perror("ftok error");return(-1);}int shmid = shmget(key,4096,IPC_CREAT);if(shmid<0){perror("shmget error");return(-2);}char* addr = (char*)shmat(shmid,nullptr,0);sleep(1);for(int i=0;i<26;i++){printf("read#: %s\n",addr);sleep(1);}shmdt(addr);int n = shmctl(shmid,IPC_RMID,NULL);if(n<0){perror("shmctl error");return(-3);}return 0;
}