共享内存是最快的可用IPC形式:
它允许多个不相关的Process去访问同一部分逻辑内存。
如果需要在两个运行的Process之间传输数据,共享内存跟FIFO(命名管道),文件共享文件相比是效率极高的一种方法。
一旦内存区映射到共享它的Process的地址空间,这些Process间的数据传递就不会再涉及到内核,这样可以减少系统调用时间,提高了效率。
共享内存是IPC为一个进程创建一个特殊的地址范围,它将出现在该Process的地址空间。
其他Process可以通过把同一段内存逻辑上连接到自己的地址空间。
所有的Process都可以访问共享内存的地址。
如果一个Process修改了共享内存的内容, 其他Process马上就可以看到。
共享内存本生没有任何的同步功能,所以对于共享内存的同步控制问题由“程序猿”自行负责。
可选的同步方式可以是互斥锁/条件变量/读写锁/记录锁/信号灯等等。
以下代码用命名信号量同步。
因为命名信号量在linux系统中有独立可见的文件,所以用在两个没有血缘关系的Process之间,二无名信号量在文件系统中并没有实例文件,
所以大多用在线程同步,所以以下代码采用命名信号量。
//=================================================================================================//
memory.h文件
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <stdlib.h>
#define PRINT( fmt, arg... ) printf( fmt, ##arg )
#define SHARMEM "ShareMemory"
#define SEMNAME "SemLock"
#define MAPLEN ( 2 * 1024 ) //共享内存大小
typedef struct
{
int ShmFd; //共享内存对应的文件描述符,可以考虑下跟常规文件的区别
char *ShmPtr; //共享区地址
sem_t *SemPtr; //命名信号量
}SharedMemory;
......
//=================================================================================================//
memory.c文件
#include "memory.h"
sem_t *SemOpen( char *SemName, int OpenMode, int CreateMode, int InitVal )
{
sem_t *SemPtr;
char Buf[ 40 ] = "/dev/shm/sem.";
strcat( Buf, SemName );
//判断信号量文件在系统中是否存在
{
SemPtr = sem_open( SemName, OpenMode | O_CREAT, CreateMode, InitVal );
if( NULL == SemPtr )
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
goto SemOpenOut;
}
}
else
{
SemPtr = sem_open( SemName, OpenMode );
if( NULL == SemPtr )
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
goto SemOpenOut;
}
}
SemOpenOut:
return SemPtr;
}
void SemDelete( char *SemName, sem_t *SemPtr )
{
char Buf[ 40 ] = "/dev/shm/sem.";
strcat( Buf, SemName );
if( NULL != SemPtr )
{
sem_close( SemPtr );
}
if( 0 == access( Buf, F_OK ) )
{
sem_unlink( SemName );
}
}
//===================共享内存部分代码===================//
int ShmOpen( char *ShmName, int OpenMode, int CreateMode )
{
int ShmFd = 0;
char Buf[ 40 ] = "/dev/shm/";
strcat( Buf, ShmName );
//判断共享内存文件是否存在if( 0 != access( Buf, F_OK ) )
{
ShmFd = shm_open( ShmName, OpenMode | O_CREAT, CreateMode );
if( ShmFd < 0 )
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
goto ShmOpenOut;
}
}
else
{
ShmFd = shm_open( ShmName, OpenMode, CreateMode );
if( ShmFd < 0 )
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
goto ShmOpenOut;
}
}
ShmOpenOut:
return ShmFd;
}
void ShmDelete( char *ShmName, int ShmFd )
{
char Buf[ 40 ] = "/dev/shm/";
strcat( Buf, ShmName );
if( ShmFd > 0 )
{
close( ShmFd );
}
if( 0 == access( Buf, F_OK ) )
{
shm_unlink( ShmName );
}
}
//映射共享内存
{
char *MapAddr = NULL;
//注意该处ftruncate的功能,设置共享内存文件的大小,必须大于等于共享内存的大小
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
goto ShareMemGetOut;
}
MapAddr = ( char * )mmap( MapAddr, MapLen, MapFrotect, MapFlag, *MemFd, 0 );
if( NULL == MapAddr )
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
goto ShareMemGetOut;
}
ShareMemGetOut:
return MapAddr;
}
void ShareMemDelete( char *MapAddr, int MapLen )
{
if( NULL != MapAddr )
{
if( munmap( MapAddr, MapLen ) < 0 )
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
}
}
}
//==================创建共享内存====================//
int SharedMemCreate( SharedMemory **MemPtr )
{
int ret = 0;
if( NULL == ( *MemPtr = ( SharedMemory * )malloc( sizeof( SharedMemory ) ) ) )
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
ret = -1;
goto SharedMemCreateOut;
}
//获取保护共享内存区域的信号量
if( NULL == ( ( *MemPtr )->SemPtr = SemOpen( SEMNAME, O_RDWR, 0x1a4, 1 ) ) )
{
PRINT( "%d Sem open failed!\r\n", __LINE__ );
ret = -1;
goto SharedMemCreateOut;
}
//创建并打开共享内存文件
if( ( ( *MemPtr )->ShmFd = ShmOpen( SHARMEM, O_RDWR, 0x1a4 ) ) < 0 )
{
PRINT( "%d Shm open failed!\r\n", __LINE__ );
ret = -1;
goto SharedMemCreateOut;
}
//将共享内存文件映射到当前Process
if( NULL == ( ( *MemPtr )->ShmPtr = ShareMemGet( MAPLEN, &( ( *MemPtr )->ShmFd ), 0, PROT_READ | PROT_WRITE, MAP_SHARED ) ) )
{
PRINT( "%d Shm open failed!\r\n", __LINE__ );
ret = -1;
goto SharedMemCreateOut;
}
SharedMemCreateOut:
if( -1 == ret )
{
if( NULL != *MemPtr )
{
if( NULL != ( *MemPtr )->SemPtr )
{
SemDelete( SEMNAME, ( *MemPtr )->SemPtr );
}
if( NULL != ( *MemPtr )->ShmPtr )
{
ShareMemDelete( ( *MemPtr )->ShmPtr, MAPLEN );
}
if( ( *MemPtr )->ShmFd > 0 )
{
ShmDelete( SHARMEM, ( *MemPtr )->ShmFd );
}
free( *MemPtr );
}
}
return ret;
}
void SharedMemDestroy( SharedMemory *MemPtr )
{
if( NULL != MemPtr )
{
if( NULL != MemPtr->SemPtr )
{
SemDelete( SEMNAME, MemPtr->SemPtr );
}
if( NULL != MemPtr->ShmPtr )
{
ShareMemDelete( MemPtr->ShmPtr, MAPLEN );
}
if( MemPtr->ShmFd > 0 )
{
ShmDelete( SHARMEM, MemPtr->ShmFd );
}
free( MemPtr );
}
}
//=================================================================================================//
Writememory.c文件 //将存标准输入获取的信息写入共享内存区
#include "memory.h"
int main( void )
{
SharedMemory *MemStPtr = NULL;
if( SharedMemCreate( &MemStPtr ) < 0 )
{
PRINT( "%d Shared memory create failed!\r\n", __LINE__ );
goto Out;
}
while( 1 )
{
sem_wait( MemStPtr->SemPtr );
PRINT( "memmap firt got the sem!\r\n" );
if( read( STDIN_FILENO, MemStPtr->ShmPtr, MAPLEN ) < 0 )
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
}
sem_post( MemStPtr->SemPtr );
PRINT( "memmap firt posted the sem!\r\n" );
sleep( 1 );
}
Out:
if( NULL != MemStPtr )
{
SharedMemDestroy( MemStPtr );
}
return 0;
}
//=================================================================================================//
Readmemory.c文件 //读取并打印共享内存的信息
int main( void )
{
SharedMemory *MemStPtr = NULL;
if( SharedMemCreate( &MemStPtr ) < 0 )
{
PRINT( "%d Shared memory create failed!\r\n", __LINE__ );
goto Out;
}
while( 1 )
{
sem_wait( MemStPtr->SemPtr );
PRINT( "memmap second got the sem!\r\n" );
if( write( STDIN_FILENO, MemStPtr->ShmPtr, strlen( MemStPtr->ShmPtr ) ) < 0 )
{
PRINT( "%d %s\r\n", __LINE__, strerror( errno ) );
}
sem_post( MemStPtr->SemPtr );
PRINT( "memmap second posted the sem!\r\n" );
sleep( 1 );
}
Out:
if( NULL != MemStPtr )
{
SharedMemDestroy( MemStPtr );
}
return 0;
}
//=================================================================================================//
注:信号量跟共享内存的文件并不存在于当前的文件夹下,而是在/dev/shm下。