第一种共享内存:

A、B进程通过映射将同一块物理内存映射到自己的虚拟地址空间,这样该内存里的东西对A、B进程是透明的,反之亦然。

 

对于共享内存,并没有同步机制,A进程在对其进行写操作钱并不能阻止B进程对该内存进行写操作,所以我们应该主动加上同步动作,如信号量、读写锁等

 

容器共享内存设置_内核空间


 

 获取第一种共享内存的方法:

Shmget    创建或者获取一块共享内存

Shmat 将共享内存映射到当前进程的地址空间

Shmdt 断开映射

Shmctl 对共享内存进行属性的设置   控制



使用共享内存的优点:

所以说共享内存是最快的通信方式;

2.共享内存不会像匿名管道一样,共享内存可以使用任何在任何进程之间;



使用共享内存的缺点:

没有像管道一样提供同步机制



1.简单的使用
Int main()
{
Int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);
 
Assert(shmid!=-1);
 
Char *s=(char*)shmat(shmid,NULL,0);//
Assert(s!=(char*)-1);
 
 
Strcpy(s,”hello”);
 
Shmdt(s);
 
//shmdt(s);移除
}
 
 
Int main()
{
Int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);
 
Assert(shmid!=-1);
 
Char *p=(char*)shmat(shmid,NULL,0);//
 
Assert(p!=(char*)-1);
 
Printf(“p=%s”,p);
 
Shmdt(p);
 
Exit(0);
}
 
俩个进程key要相同!!
 
 
 
 
信号量与共享内存的结合:
 
 

2.写端:
Int main()
{
Int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);
 
Assert(shmid!=-1);
 
Char *s=(char*)shmat(shmid,NULL,0);//
Assert(s!=(char*)-1);
 
 
Int a[2]={0,1};
Sem_init(a,2);
 
While(1)
{
Char buff[256]={0};
Sem_p(0);
Fgets(buff,128,stdin);
Strcpy(s,buff);
Sem_v(1);   
//只要我数据写入到共享内存就v操作  该语句放在if下面  可能程序直接break 来不及v操作
 
If(strncmp(buff,”end”,3)==0)
{
Break;
}
 
}
 
 
 
读端:
Int main()
{
Int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);
 
Assert(shmid!=-1);
 
Char *p=(char*)shmat(shmid,NULL,0);//
 
Assert(p!=(char*)-1);
 
Int a[2]={0,1};
Sem_init(a,2);//创建并初始化信号量  或者获取信号量id
 
 
While(1)
{
Sem_p(1);
If(strncmp(p,”end”,3) == 0)
{
Break;
}
Printf(“p=%s”,p);
Sem_v(0);
 
}
 
Shmdt(p);
 
Sem_del();
Exit(0);
}


普通的读写文件的原理,进程调用read或是write后会陷入内核,因为这两个函数都是系统调用,进入系统调用后,内核开始读写文件,假设内核在读取文件,内核首先把文件读入自己的内核空间,读完之后进程在内核回归用户态,内核把读入内核内存的数据再copy进入进程的用户态内存空间。实际上我们同一份文件内容相当于读了两次,先读入内核空间,再从内核空间读入用户空间。




linux会有两种方式可以使用共享内存: 

分别是shm_XX函数和mmap,这两种共享内存的是内核实现方式大同小异,但是还是有区别的.

二者的区别如下:

  • POSIX标准的是mmap,具有简单易用的特点.
  • system V标准的是shm_xx函数组(shm_get,shm_at,shm_dt,shm_ctl).
  • shm_xx函数的共享内存需要自己释放,即进程的结束不会导致共享内存的释放,他会一直保存直到手动释放或者关机.而mmap则相反,如果是非映射到文件,那么会在进程结束就结束了.
  • 当调用shm_get时,会创建一个文件并返回这个文件描述符,这个文件是内核创建的特殊的文件,它属于shm文件系统中,也就是说它的实现方式和mmap的大同小异,只是映射的文件是内核中特殊的文件系统中的文件,任何进程都可以共享.这个特殊的文件,会对应开辟的共享内存.当调用shm_at时会将该空间映射进进程的地址空间中.
  • shm_xx的特殊文件系统挂在点在/dev/shm中,也就是内存/交换分区中,所以会在系统重启后消失,里面的东西也就没了,而mmap可以映射到文件中.





第二种共享内存:



说一下mmap的三个用途:



1.将一个普通的文件映射到内存中,通常该文件是要进行频繁读写的文件,这样用内存读写取代IO读写,以获得较高的性能;



fd=open(name, flag, mode); if(fd<0) ...
ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);






2.父进程进行mmap后,进行fork,子进程就可以继承来自父进程的mmap返回的内存的起始地址,这样父子进程就可以共同维护这个特殊的文件,达到通讯;一般采用匿名映射;





3.对于无关联的进程,也可以将一个文件映射到内存,提供一个不同进程共享的空间;









最后说一下mmap和munmap的参数:



mmap就是把一个文件的内容在内存里面做一个映像。映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,同样,内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间<---->用户空间两者之间需要大量数据传输等操作的话效率是非常高的。
start:要映射到的内存区域的起始地址,通常都是用NULL(NULL即为0)。NULL表示由内核来指定该内存地址

length:要映射的内存区域的大小
prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
PROT_EXEC //页内容可以被执行
PROT_READ //页内容可以被读取
PROT_WRITE //页可以被写入
PROT_NONE //页不可访问
flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体
MAP_FIXED :使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
MAP_SHARED :对映射区域的写入数据会复制回文件内, 而且允许其他映射该文件的进程共享。
MAP_PRIVATE :建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
MAP_DENYWRITE :这个标志被忽略。
MAP_EXECUTABLE :同上
MAP_NORESERVE :不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
MAP_LOCKED :锁定映射区的页面,从而防止页面被交换出内存。
MAP_GROWSDOWN :用于堆栈,告诉内核VM系统,映射区可以向下扩展。
MAP_ANONYMOUS :匿名映射,映射区不与任何文件关联。



(当该参数生效时,实际上真正的映射了一块物理内存,而不是一个文件)
MAP_ANON :MAP_ANONYMOUS的别称,不再被使用。
MAP_FILE :兼容标志,被忽略。
MAP_32BIT :将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
MAP_POPULATE :为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
MAP_NONBLOCK :仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。

fd:文件描述符(由open函数返回)

offset:表示被映射对象(即文件)从那里开始对映,通常都是用0。 该值应该为大小为PAGE_SIZE的整数倍

返回说明
成功执行时,mmap()返回被映射区的指针,munmap()返回0。失败时,mmap()返回MAP_FAILED[其值为(void *)-1],munmap返回-1。errno被设为以下的某个值
EACCES:访问出错
EAGAIN:文件已被锁定,或者太多的内存已被锁定
EBADF:fd不是有效的文件描述词
EINVAL:一个或者多个参数无效
ENFILE:已达到系统对打开文件的限制
ENODEV:指定文件所在的文件系统不支持内存映射
ENOMEM:内存不足,或者进程已超出最大内存映射数量
EPERM:权能不足,操作不允许
ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志
SIGSEGV:试着向只读区写入
SIGBUS:试着访问不属于进程的内存区






start:要取消映射的内存区域的起始地址
length:要取消映射的内存区域的大小。
返回说明
成功执行时munmap()返回0。失败时munmap返回-1.
int msync(const void *start, size_t length, int flags);

对映射内存的内容的更改并不会立即更新到文件中,而是有一段时间的延迟,你可以调用msync()来显式同步一下, 这样你内存的更新就能立即保存到文件里
start:要进行同步的映射的内存区域的起始地址。
length:要同步的内存区域的大小
flag:flags可以为以下三个值之一:
MS_ASYNC : 请Kernel快将资料写入。
MS_SYNC : 在msync结束返回前,将资料写入。
MS_INVALIDATE : 让核心自行决定是否写入,仅在特殊状况下使用