“共享内存”(shared memory)可以定义为对一个以上的进程是可见的内存或存在于多个进程的虚拟地址空间,是一种高效的进程间通信方式。
为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。
不过共享内存往往不是单独使用的,一般为了多进程间访问共享内存,还需要考虑进程间的同步问题,所以共享内存的使用往往需要配合着消息机制进行同步。
一、共享内存的使用实现步骤:
1、进程A创建共享内存:调用内存映射API函数CreateFileMapping创建一个有名字标识的共享内存:
#define FILEMAPPING_NAME "mytestshare"
#define FILESIZE 4096
HANDLE hmap =CreateFileMappingA(I
NVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE | SEC_COMMIT,
0,
FILESIZE,
FILEMAPPING_NAME
);
2、创建文件映射对象后,进程需要调用MapViewOfFile函数映射到本进程的地址空间中:
LPVOID lpdata = MapViewOfFile(
hmap,
FILE_MAP_READ | FILE_MAP_WRITE,
0,
0,
0
);
这里进程A就可以向lpdata中写入数据。
3、此时,另一个进程B可以打开指定的文件映射对象:
HANDLE hmapfile =OpenFileMappingA(
FILE_MAP_READ,
FALSE,
FILEMAPPING_NAME
);
4、另一进程B将我呢见映射对象映射到当前应用程序的地址空间:
LPVOID lpbase = MapViewOfFile(
hmapfile,
FILE_MAP_READ,
0,
0,
0
);
此时,进程B就可以读取到共享内存中的数据了。
5、当进程结束使用共享内存后,调用UnmapViewOfFile函数取消映射:
UnmapViewOfFile(lpbase);//解除映射
6、最后关闭映射:
CloseHandle(hmapfile);//一个指定的文件映射对象
以上就是一个最简单的共享内存的API使用。
二、共享内存函数的高级使用:
一般我们在创建文件映射对象,都是向上面一样的参数创建,但在有些场景下,这种方式创建的共享内存,其他的进程可能无法使用,比如system、管理员用户创建的共享内存,标准用户无权限访问。此时就需要我们深入了解CreateFileMapping这个函数:
HANDLECreateFileMapping(
HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName
);
参数:
1、 hFile:映射文件的句柄,若设为INVALID_HANDLE_VALUE(0xFFFFFFFF),则创建一个进程间共享的对象;
2、lpFileMappingAttributes:安全属性,此属性一般情况下,设置为NULL,表示默认安全对象,此处就关系到了不同用户权限进程是否可以访问创建的共享内存了;
3、flProtect:保护文件视图:
PAGE_EXECUTE_READ:映射将允许只读、写时复制或执行方式访问。
PAGE_EXECUTE_READWRITE:映射将允许只读、写时复制、读写或执行方式访问。
PAGE_EXECUTE_WRITECOPY:映射将允许自读、写时复制或执行方式访问,相当于第一个。
PAGE_READONLY:映射将允许只读、写时复制方式访问。
PAGE_READWRITE:映射将允许可读、写时复制读写方式访问。
PAGE_WRITECOPY:映射将允许只读或写时复制方式访问。
可组合使用下述一个或多个常数:
SEC_COMMIT:为文件映射一个小节中的所有页分配内存
SEC_IMAGE:文件是个可执行文件
SEC_RESERVE:为没有分配实际内存的一个小节保留虚拟内存空间
...
4、dwMaximumSizeHigh:文件映射对象的大小,高32位。
5、dwMaximumSizeLow:文件映射对象的大小,低32位。
6、lpName:映射文件名,即共享内存的名称。
返回值:
函数成功,返回句柄,否则返回NULL,GetLastError函数获取错误码。
现在我们来看看如何创建一个全局的共享内存,即system用户进程、管理员用户进程、其他用户进程都可以访问的共享内存。
const char* shareName = "Global\\testGlobalMemory";
const char* shareSSDL = "D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GA;;;IU)";
//创建安全描述符
SECURITY_ATTRIBUTES security;
ZeroMemory(&security, sizeof(security));
security.nLength = sizeof(security);
ConvertStringSecurityDescriptorToSecurityDescriptorA(
shareSSDL ,
SDDL_REVISION_1,
&security.lpSecurityDescriptor,
NULL);
//以指定的安全描述符去创建共享内存
HANDLE tmp = CreateFileMappingA(INVALID_HANDLE_VALUE, &security,
PAGE_READWRITE, 0, SHAMEM_MAX, shareName );
LocalFree(security.lpSecurityDescriptor);
下面就来解释一下为什么这么做。
如果我们是system用户进程调用CreateFileMappingA函数创建一个共享内存,此时对于用户权限比他低的普通标准用户来说,是无权访问这个共享内存的,因为安全描述符继承的system用户的。
此时我们就需要自己来创建一个对于标准用户可访问的安全描述符,那一串字符串就是字符串格式的安全描述符,windows官方定义如下:
https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format
https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-strings
这里面的内容还是挺多的,这里我就简单介绍这里使用的:
D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GA;;;IU)
D代表DACL,P代表DACL flags
括号里的就是ACE字符串,格式如下:
ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid;(resource_attribute)
如第一个括号,A代表SDDL_ACCESS_ALLOWED;
OICI代表SDDL_OBJECT_INHERIT|SDDL_CONTAINER_INHERIT;
GA代表的是SDDL_GENERIC_ALL,也就是读写执行权限都有;
SY代表的是SDDL_LOCAL_SYSTEM,即local system用户;
这个字符串的大概意思就是local sysem用户具有读写执行权限;
第二个字符串BA是内置管理员,即内置管理员具有读写执行权限;
第三个字符串IU是交互式登录用户,这里也是GA,即具有读写执行权限,如果这里设置的是GR,则对于交互式登录用户来说,就只有读权限了,以读写方式访问共享内存,则会被拒绝。
转载请联系,并说明原文地址:https://blog.csdn.net/anranjingsi/article/details/116070796