共享内存
共享内存区是最快的IPC形式,一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核。
前几种,内核提供资源,代码借助内核,从用户态切换到内核态,完成之后,又要从内核态切换到用户态。
共享内存是通过MMU机制,直接映射到内核。
共享内存的特点
- 双向通信,可用于随意的进程,不一定非要有亲缘关系
- 没有面向字节流或者数据报的概念,就是一块内存,可以随机访问
- 生命周期随内核,需要人为显式删除共享内存或者重启内核。
- 没有内置同步互斥机制
查看系统中存在的共享内存
ipcs -m
删除系统中存在的共享内存
ipcrm -m + shmid
和前面两种进程间通信类似,共享内存也有一个专门的结构来描述它。
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 : 创建的共享内存的大小,以 4k(页表的大小) 大小为单位进行分配空间。
shmflg : 标志位
返回值:
成功返回一个非负整数,该段的共享内存的标志码,失败则返回-1
shmat函数
原型: void *shmat(int shmid, const void *shmaddr, int shmflg);
功能: 将共享内存映射到进程地址空间
参数:
shmid : 要映射的共享内存的标识
shmaddr :指定连接的地址,一般都是填NULL,让操作系统自动映射
shmflg :标志位
返回值:
成功返回一个指针,指向共享内存的第一个字节,失败返回-1
/* mode for attach */
#define SHM_RDONLY 010000 /* read-only access */
#define SHM_RND 020000 /* round attach address to SHMLBA boundary */
#define SHM_REMAP 040000 /* take-over region on attach */
#define SHM_EXEC 0100000 /* execution access */
shmdt函数
原型 :int shmdt(const void *shmaddr);
功能 :将共享内存段与当前进程脱离,并不是删除这段共享内存,只是脱离映射关系
参数 :
shmaddr :指向attach后的地址空间
返回值 :
成功返回0, 失败返回 -1
shmctl函数
原型 :int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能 : 控制共享内存
参数 :
shmid :共享内存的标识码
cmd :要做什么操作 (IPC_STAT、IPC_SET、IPC_RMID)
buf :指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值 :
成功返回0,失败返回-1
cmd :
IPC_STAT :把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET :在进程有足够权限前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID :删除共享数据段
common.h
用于封装为功能性更完整的函数
#pragma once
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#define PATHNAME "."
#define PROJ_ID 0x1
int CreateShm(size_t size); // 创建一个共享内存
int GetShm(); //获取一个共享内存
int DestroyShm(int shmid); //销毁一个共享内存
common.c
#include "common.h"
//由于创建和获取的代码基本上一致,就只有shmget的参数有些不同。
//所以就将其封装。将shmget的参数作为封装好的函数的参数
int CommonShm(size_t size, int flags)
{
key_t key = ftok(PATHNAME, PROJ_ID);
if(key < 0)
{
perror("ftok");
return -1;
}
int shmid = shmget(key, size, flags);
if(shmid < 0)
{
perror("shmget");
return -1;
}
return shmid;
}
//创建一个共享内存
int CreateShm(size_t size)
{
return CommonShm(size, IPC_CREAT | IPC_EXCL | 0666);
}
//获取一个共享内存
int GetShm()
{
return CommonShm(0,IPC_CREAT);
}
//销毁一段共享内存
int DestroyShm(int shmid)
{
int ret = shmctl(shmid, IPC_RMID, NULL);
if(ret < 0)
{
perror("shmctl");
return -1;
}
return 0;
}
server.c
服务器端代码
#include "common.h"
int main()
{
int shmid = CreateShm(1024);
if(shmid < 0)
{
printf("CreateShm");
return -1;
}
printf("shmid : %d\n",shmid);
char *ptr = (char*)shmat(shmid, NULL, SHM_RDONLY);
while(1)
{
printf("%s\n", ptr);
sleep(1);
}
shmdt(ptr);
return 0;
}
client.c
客户端代码
#include "common.h"
int main()
{
int shmid = GetShm();
if(shmid < 0)
{
printf("GetShm");
return -1;
}
printf("shmid: %d\n",shmid);
// 0 表示
char *ptr = (char*)shmat(shmid, NULL, 0);
int i = 1;
while(1)
{
int j = 0;
while(j < i)
{
ptr[j] = 'A' + i - 1;
j++;
}
ptr[j] = '\0';
i++;
i %= 26;
sleep(1);
}
shmdt(ptr);
return 0;
}