目录
一:共享内存
1.1定义
内核管理一片物理内存,允许不同的进程同时映射(访问),多个进程可以映射同一块内存。被多个进程同时映射的物理内存:即共享内存。(允许多进 程同步访问给定的储存区域)。
映射物理内存-》挂接 ; 用完之后解除映射-》脱接。
1.2优缺点
1.2.1 优点
因为数据不需要在客户进程和服务器进程之间复制,所以这是一种最快的IPC。
1.2.2缺点
内核没有提供对共享内存的互斥访问。(信号量,互斥量等)
1.3共享内存结构维护
内核为每个共享内存都维护相关信息:
//共享内存的维护信息
struct shmid_ds{
struct ipc_perm shm_perm; //权限控制
size_t shm_segsz; //size of segement in bytes 字节数
pid_t shm_lpid; //pid of last shomp() 最后执行shomp()的进程ID
pid_t shm_cpid; //创建该共享内存的ID
shmatt_t shm_nattch; //number of current attaches,现有连接数
time_t shm_time; //最后被连接时间
time_t shm_dtime; //最后删除连接的时间
time_t shm_ctime; //最后修改时间
.
.
}
1.4共享内存的通信原理
Linux,中每个进程都有自己的PCB(进程控制块)和Addr Space(地址空间),该虚拟地址空间通过多级页表(这里以一级页表为例)和物理地址空间进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这个块区域即:共享内存。
对于一个共享内存,实现采用的是引用计数的原理,当进程脱接共享储存区后,计数器减一;挂接则加一,直至计数器变为0,该共享内存才被删除。当进程终止的时间,它所附加的共享储存区都会自动脱离。
这里还可以解决一个问题:为什么共享内存速度最快?
上图中:Proc A进程在给共享区域写数据,Proc B 进程从其中读取数据,再次其中一共发生了两次数据的复制:
(1):Prco A 到共享内存 (2) : 共享内存到 Prec B
因为直接在内存上操作,所以共享内存的速度也就提高了。
二:相关函数接口
2.1 Linux命令
2.1.1查看系统中的共享储存段
ipcs -m
2.1.2删除系统中的共享储存段
ipcrm -m [shmid]
2.2 函数
2.2.1shmget()创建共享内存函数
函数头文件 函数原型: |
#include<sys/shm.h> int shmget ( key_t key , size_t size , int flag ); |
函数参数: | 1. key:将key变换成一个标识符 2.size_t:创建:共享存储段的长度,字节为单位。引用:设置为0 3.flag : 控制位。 |
函数功能: | 若key不存在,创建一个;否则获取。 成功返回 共享储存的ID,失败返回-1; |
2.2.2shmctl()销毁等 函数
函数原型: | int shmctl ( int shmid , int cmd , struct shmid_ds *buf); |
函数参数: | 1.shmid:指定共享内存 2.cmd :在shmid上执行某些命令:IPC_STAT IPC_SET IPC_RMID SHM_LOCK PID_UNLOCK |
函数功能: | 函数对共享存储段执行多种操作。 成功返回0,失败返回-1. |
2.2.3shmat()脱接共享内存函数
函数原型: | void* shmat ( int shmid , const void *addr , int flag ); | ||||||
函数参数: | addr:指定共享储存段连接到调用进程的哪个地址上。
|
||||||
函数功能: | 一旦创建了一个共享储存段,进程就可以调用shmat将其连接到它的地址空间中 |
2.2.4shmdt()去关联共享内存函数
函数原型和返回值: | int shmdt ( const void *shmaddr ); 成功返回0,并将shmid_ds结构体中的shm_nattch计数器减一;出错返回-1。 |
参数: | 连接以后返回的地址 |
功能: | 当一个进程不需要共享内存的时间,就需要 去关联。该函数并不删除所指定的共享存储区,而是将之前用shmat函数连接好的共享内存区 脱离目前进程。 |
三:代码测试
sem:链接https://blog.csdn.net/genzld/article/details/83095577
3.1shma.c文件
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include <sys/shm.h> // shm ==> shared memory
#include "./sem.h"
int main()
{
int shmid = shmget((key_t)1234, 512, 0666 | IPC_CREAT);//指定共享内存空间的大小
assert(shmid != -1);
char *ptra = (char *)shmat(shmid, NULL, 0);//推荐给NULL,使得系统自动选取虚拟地址
//ptra指向的是内核对象的空间
assert(ptra != (char*)-1);
int semid = sem_init(123);//初始化一个信号量,
while(1)
{
//sem_p(semid, 0);
printf("please input: ");
fflush(stdout);
memset(ptra, 0, 512);//内核对象中的内容全部清空为0
fgets(ptra, 128, stdin);//往共享内存中存取信息
ptra[strlen(ptra) - 1] = 0;//回车替换
if(strcmp(ptra, "end") == 0)
{
break;
}
// sem_v(semid, 0);
}
shmdt(ptra);//shmdt(void* addr)//给shmat的返回值,将虚拟地址和内存空间断开
}
3.2shmb.c文件
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include <sys/shm.h> // shm ==> shared memory
#include "./sem.h"
int main()
{
int shmid = shmget((key_t)1234, 512, 0666 | IPC_CREAT);
assert(shmid != -1);
char *ptrb = (char *)shmat(shmid, NULL, 0);//自动查找
assert(ptrb != (char*)-1);
int semid = sem_init(123);
while(1)
{
//sem_p(semid, 0);
if(strcmp(ptrb, "end") == 0)
{
break;
}
int i = 0;
for(; i < strlen(ptrb); ++i)//读取ptrb中的内容
{
printf("%c\n", ptrb[i]);
sleep(1);
}
//sem_v(semid, 0);
}
shmdt(ptrb);
}