一、基本概念
什么是共享内存,顾名思义,就是将共享一片内存空间,共享内存允许多个不同的进程访问同一片内存空间。他们对这个内存直接进行操作,不需要经过内核的处理,因此共享内存是IPC通信方式中效率最高的。那如何实现的呢,只要你想访问这片内存空间,你得先把这片内存空间映射到你的内存中,然后再对其进行操作。
但是,这种内存空间我们要对其进行维护,保护的一个操作,例如:进程A往共享内存存数据,而此时进程B刚好在读数据,那B读取的数据就可能会出现数据混乱,因此,共享内存属于临界资源,我们在操作它时要进行保护操作,即在某一个时刻,只有一个进程对其进行读/写操作,防止数据出现混乱。所以共享内存一般不会单独的进行使用,要配合信号量、互斥锁等来进行使用,目的就是保护数据的完整性。
特点:1)共享内存是进程间通信效率最高的方式之一
2)共享内存可传输的数据量比较大,使用共享内存一般是以传输数据为目的的
3)读取过的内容不会删除,某一个进程修改共享内存空间的数据后,其他进程可以察觉这个修改
4)无同步无互斥,需要信号量配合。
二、基本使用流程
1)创建或获取共享内存
int shmget(key_t key, size_t size, int shmflg);
key的取值:
① 通过调用key_t ftok(const char *pathname, int proj_id)来进行获取;这个函数是通过指定的文件路径和计划编号合成一个key值。
② IPC_PRIVATE 内核会保证创建一个新的,唯一的IPC对象,这是一个宏定义,其值为0。
size:指定共享内存的大小,是以页为单位的,即使存1个字节,也会分配一整页。
shmflg:IPC_CREAT 若系统中有相同的key值,则返回共享内存的标识符;若不存在则创建,要与 0600 结合 给相应的权限(IPC_CREAT | 0600)。
IPC_EXCL 如果系统中有相同的key值,则会报错;不存在则创建。
2)将共享内存映射到当前进程中
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid:就是创建共享内存时返回的描述符。
shmaddr:一般为NULL,系统会帮你自动选择一个内存空间去分配。
shmflg:操作共享内存的方式:
SHM_RDONLY:以只读方式打开。
SHM_EXEC:具有执行的权限。
SHM_REMAP:重新映射,此时shmaddr不能为空。
3)操作共享内存
直接针对映射后返回的指针进行操作,给指针进行赋值。
4)断开共享内存映射
int shmdt(const void *shmaddr);
shmaddr:映射的共享内存的地址。
成功返回0,失败返回-1,并将错误记录。
5)释放共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:共享内存的标识符
cmd:常用的控制命令如下:
IPC_RMID:删除该共享内存
IPC_STAT:获取属性权限,放到buf中
IPC_SET:设置属性信息为buf指向的内容。
IPC_INFO:获得关于共享内存的系统限制值信息。
SHM_INFO:获得系统为共享内存消耗的资源信息。
buf:在释放共享内存时,为NULL即可。
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
struct ipc_perm {
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and
SHM_LOCKED flags */
unsigned short __seq; /* Sequence number */
};
三、代码示例
一个进程负责读取你的个人信息并写入到共享内存,另一个进程负责将共享内存中的个人信息进行打印
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
struct info
{
char name[50];
int age;
char address[100];
}DATA;
int main()
{
struct info data = {"zhangsan",22,"xian"};
key_t key = ftok("/bin/bash",1);
if(key == -1)
{
perror("failed1");
return -1;
}
//创建一个共享内存
int shmid = shmget(key,sizeof(struct info),IPC_CREAT | 0600);
if(shmid == -1)
{
perror("failed2");
return -1;
}
//进行共享内存的映射
char * buf = (char*)shmat(shmid,NULL,0);
if(*(int*)buf == -1 )
{
perror("failed3");
return -2;
}
pid_t pid = fork();
//子进程负责读共享内存的数据并打印
if(pid == 0)
{
sleep(3);
printf("%s\n",buf);
shmdt(buf);
exit(0);
}
//父进程往共享内存中写入数据
else if(pid >0)
{
char arr[5] ="";
snprintf(arr,sizeof(arr),"%d",data.age);
snprintf(buf,sizeof(struct info),"%s %s %s",data.name,arr,data.address);
wait(NULL);//等待子进程结束
shmdt(buf);//关闭内存映射
shmctl(shmid,IPC_RMID,NULL);//释放内存资源
}
return 0;
}