Instance analysis of interprocess communication of Shared memory under win32

  • 2020-04-02 02:24:36
  • OfStack

An overview,

In many cases, in a Windows program, data is exchanged and communicated between processes. The WIN32 API provides a number of functions that allow us to communicate easily and efficiently between processes, through which we can control the exchange of data between different processes.

There are many ways of interprocess communication and data exchange: message, Shared memory, anonymous (named) pipes, mail slots, Windows sockets, and many other technologies. Shared memory can be defined as memory that is visible to more than one process or as a virtual address space that exists in multiple processes. For example, if two processes use the same DLL and load the DLL's code page into memory only once, all other processes that map the DLL need only share the code pages. Although IPC has some disadvantages such as small amount of data exchanged and little information carried by message mechanism, it is widely used in internal process communication system without large amount and frequent data exchange because of its convenience and flexibility.

Two, the realization of Shared memory between processes of the same machine

Using memory-mapped files to communicate between WIN32 processes: the memory-mapped file mechanism in Windows provides a way for us to efficiently manipulate files. It allows us to keep a section of memory in the WIN32 process and map the target file on the hard disk or page file to this section of virtual memory. Note: synchronization between processes must be considered in the program implementation.

The specific implementation steps are as follows:

1. Call the memory mapping API function CreateFileMapping in the server-side process to create a named Shared memory;

The prototype of the function CreateFileMapping is as follows:


HANDLE CreateFileMapping (
HANDLE hFile, //Handle to the mapping file that if set to 0xFFFFFFFF(that is, INVALID_HANDLE_VALUE) creates an object that is Shared between processes
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, //Security attribute
DWORD flProtect, //Protect the way
DWORD dwMaximumSizeHigh, //Size of object
DWORD dwMaximumSizeLow, 
LPCTSTR lpName //Map file name, that is, the name of Shared memory
);

Similar to virtual memory, the protection parameter can be either PAGE_READONLY or PAGE_READWRITE. If multiple processes all have write access to the same Shared memory, they must keep in sync with each other. The mapping file can also specify the PAGE_WRITECOPY flag, which ensures that its original data is not corrupted, while allowing other processes to manipulate copies of the data freely if necessary.

For example: create a named mapping file with a length of 4096 bytes called "ZZJ" :


HANDLE m_hMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x1000," zzj");

2. After creating the file mapping object, the server-side process calls the MapViewOfFile function to map to the address space of the process;
Example: map cache view


void* m_pBaseMapFile=MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

3. When the client process accesses the Shared memory object, it needs to call the OpenFileMapping function through the memory object name to get a handle to the Shared memory object


HANDLE m_hMapFile =OpenFileMapping(FILE_MAP_WRITE,FALSE," zzj"); 

4. If the client process successfully obtains a handle to the Shared memory object, the MapViewOfFile function is called to map the object view. Users can use this object view to read and write data to achieve the purpose of data communication.
Example: map cache view


void* m_pBaseMapFile=MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

5. When the user process ends using the Shared memory, the UnmapViewOfFile function is called to cancel the view in its address space:


if (m_pBaseMapFile) 
{ 
  UnmapViewOfFile(m_pBaseMapFile); 
  SharedMapView=NULL; 
}

Third, the use of file mapping to achieve Shared memory.

FileMapping used to exist in the disk File into the virtual address space of a process, and in the process of virtual address space of a region to "hold" the File, this space is called the File View (in process of virtual memory), system and at the same time to create a File Mapping Object (stored in physical memory) is used to maintain the Mapping relations, such as multiple processes need to read and write the File data, In fact, their File views correspond to the same File Mapping Object, which can save memory and maintain synchronization of data, and achieve the purpose of data sharing.

Of course, when an application writes data to a file, other processes should not read the data being written. This requires some synchronization. Let's look at the specific API.

Usage of CreateFileMaping:


HANDLE CreateFileMapping( //Returns a handle to the FileMapping Object
HANDLE hFile, //Handle to the file that you want to generate the mapping to
LPSECURITY_ATTRIBUTES lpAttributes, //Security properties (for NT and 2000 only)
DWORD flProtect, //Protect the Peugeot
DWORD dwMaximumSizeHigh, //Store in a high position of DWORD
File Mapping Object //The size of the
DWORD dwMaximumSizeLow, //Store in a low place in DWORD
File Mapping Object //The size of the Usually one of these two parameters is 0 ) 
LPCTSTR lpName //Name of the File Mapping Object.
);

1) Physical file handle

Any physical file handle that is available, if you need to create a memory map that is unrelated to the physical file, set it to 0xFFFFFFFF(INVALID_HANDLE_VALUE).

If you need to be associated with a physical file, make sure that the access mode of your physical file when it is created matches the "protection Settings". For example, if the physical file is read-only, the memory map needs to be read and written. It is recommended that your physical files be created exclusively.

If you use INVALID_HANDLE_VALUE, also need to set up the need to apply for the size of the memory space, whether physical file handle parameters effectively, so CreateFileMapping can create a has nothing to do with the physical file size of the memory space for you, even more than the actual file size, if you have a physical file effectively, and the size parameters of 0, it returns to you is a memory space and the physical file size address range. The file map address space returned to you can be copied, integrated, or named, starting at 0.

2) Protection Settings

Is the security Settings, but generally set NULL is ok, using the default security configuration.

3) High file size

32 bit address space, set to 0.

4) Shared memory name

Names can contain "Global" or "Local" prefixes in the Global or session Name space primary file mappings. Other sections can contain any characters other than (), refer to the Kernel Object Name Spaces.

5) Error corresponding to GetLastError when CreateFileMapping is called

ERROR_FILE_INVALID should be reported if you attempt to create a zero-length file map
ERROR_INVALID_HANDLE if you find that your named memory space has the same name as an existing memory map, mutex, semaphore, or critical section
ERROR_ALREADY_EXISTS indicates that the memory space name already exists

Use the function CreateFileMapping to create a handle to the file data that you want to share, then use the MapViewOfFile to get the Shared memory address, and then OpenFileMapping to open the name of the Shared file in another process so that different processes can share the data.

This program includes a client and a server, the server creates Shared memory, the client opens Shared memory, both access Shared memory through two events exclusive, to achieve a small function, is the server process read data from the console sent to the client process.

Server:


#include "stdafx.h" 
#include <Windows.h> 
#include <iostream> 
using namespace std; 
 
int main() 
{ 
  HANDLE hMutex      = NULL; 
  HANDLE hFileMapping   = NULL; 
  LPVOID lpShareMemory  = NULL; 
  HANDLE hServerWriteOver = NULL; 
  HANDLE hClientReadOver = NULL; 
 
  //create share memory 
  hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, 
    NULL, 
    PAGE_READWRITE, 
    0, 
    1024*1024, 
    L"ShareMemoryTest"); 
  if (NULL == hFileMapping) 
  { 
    cout << "CreateFileMapping fail:" << GetLastError() << endl; 
    goto SERVER_SHARE_MEMORY_END; 
  } 
 
  lpShareMemory = MapViewOfFile(hFileMapping, 
    FILE_MAP_ALL_ACCESS, 
    0, 
    0,   //memory start address 
    0);   //all memory space 
  if (NULL == lpShareMemory) 
  { 
    cout << "MapViewOfFile" << GetLastError() << endl; 
    goto SERVER_SHARE_MEMORY_END; 
  } 
 
  hMutex = CreateMutex(NULL, FALSE, L"SM_Mutex"); 
  if (NULL == hMutex || ERROR_ALREADY_EXISTS == GetLastError()) 
  { 
    cout << "CreateMutex" << GetLastError() << endl; 
    goto SERVER_SHARE_MEMORY_END; 
  }//Multiple threads mutually exclusive access
 
  //send data 
  hServerWriteOver = CreateEvent(NULL, 
    TRUE, 
    FALSE, 
    L"ServerWriteOver"); 
  hClientReadOver = CreateEvent(NULL, 
    TRUE, 
    FALSE, 
    L"ClientReadOver"); 
  if (NULL == hServerWriteOver || 
    NULL == hClientReadOver) 
  { 
    cout << "CreateEvent" << GetLastError() << endl; 
    goto SERVER_SHARE_MEMORY_END; 
  } 
 
  char p = 0; 
  char* q = (char*)lpShareMemory; 
  do  
  { 
    p = getchar(); 
    if (WaitForSingleObject(hClientReadOver, 5*1000) != WAIT_OBJECT_0)  
      goto SERVER_SHARE_MEMORY_END; 
    q[0] = p; 
    if (!ResetEvent(hClientReadOver)) goto SERVER_SHARE_MEMORY_END;//Sets the specified event object to a semaphore state
    if (!SetEvent(hServerWriteOver)) goto SERVER_SHARE_MEMORY_END;//Sets the specified event object to the signaled state
  } while (p != 'n'); 
 
SERVER_SHARE_MEMORY_END: 
  //release share memory 
  if (NULL != hServerWriteOver)  CloseHandle(hServerWriteOver); 
  if (NULL != hClientReadOver)  CloseHandle(hClientReadOver); 
  if (NULL != lpShareMemory)   UnmapViewOfFile(lpShareMemory); 
  if (NULL != hFileMapping)    CloseHandle(hFileMapping); 
  if (NULL != hMutex)       ReleaseMutex(hMutex); 
  return 0; 
} 

Client:


#include "stdafx.h" 
#include <Windows.h> 
#include <iostream> 
using namespace std; 
int main() 
{ 
  HANDLE hMutex      = NULL; 
  HANDLE hFileMapping   = NULL; 
  LPVOID lpShareMemory  = NULL; 
  HANDLE hServerWriteOver = NULL; 
  HANDLE hClientReadOver = NULL; 
 
  hMutex = OpenMutex(MUTEX_ALL_ACCESS, 
    FALSE, 
    L"SM_Mutex"); 
  if (NULL == hMutex) 
  { 
    if (ERROR_FILE_NOT_FOUND == GetLastError()) 
    { 
      cout << "OpenMutex fail: file not found!" << endl; 
      goto CLIENT_SHARE_MEMORY_END; 
    } 
    else 
    { 
      cout << "OpenMutex fail:" << GetLastError() << endl; 
      goto CLIENT_SHARE_MEMORY_END; 
    } 
  } 
 
  if (WaitForSingleObject(hMutex, 5000) != WAIT_OBJECT_0)//HMutex returns once the mutex object is in a signaled state
  { 
    DWORD dwErr = GetLastError(); 
    goto CLIENT_SHARE_MEMORY_END; 
  } 
 
  //open share memory 
  hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, 
    FALSE, 
    L"ShareMemoryTest"); 
  if (NULL == hFileMapping) 
  { 
    cout << "OpenFileMapping" << GetLastError() << endl; 
    goto CLIENT_SHARE_MEMORY_END; 
  } 
 
  lpShareMemory = MapViewOfFile(hFileMapping, 
    FILE_MAP_ALL_ACCESS, 
    0, 
    0, 
    0); 
  if (NULL == lpShareMemory) 
  { 
    cout << "MapViewOfFile" << GetLastError() << endl; 
    goto CLIENT_SHARE_MEMORY_END; 
  } 
 
  //read and write data 
  hServerWriteOver = CreateEvent(NULL, 
    TRUE, 
    FALSE, 
    L"ServerWriteOver"); 
  hClientReadOver = CreateEvent(NULL, 
    TRUE, 
    FALSE, 
    L"ClientReadOver"); 
  if (NULL == hServerWriteOver || 
    NULL == hClientReadOver) 
  { 
    cout << "CreateEvent" << GetLastError() << endl; 
    goto CLIENT_SHARE_MEMORY_END; 
  } 
 
  char p = 0; 
  char* q = (char*)lpShareMemory; 
  do  
  { 
    if (!SetEvent(hClientReadOver))  
      goto CLIENT_SHARE_MEMORY_END; 
 
    if (WaitForSingleObject(hServerWriteOver, INFINITE) != WAIT_OBJECT_0)  
      goto CLIENT_SHARE_MEMORY_END;  
 
    p = q[0]; 
    putchar(p); 
    if (!ResetEvent(hServerWriteOver))  
      goto CLIENT_SHARE_MEMORY_END; 
  } while (p != 'n'); 
 
CLIENT_SHARE_MEMORY_END: 
  //release share memory 
  if (NULL != hServerWriteOver)  CloseHandle(hServerWriteOver); 
  if (NULL != hClientReadOver)  CloseHandle(hClientReadOver); 
  if (NULL != lpShareMemory)   UnmapViewOfFile(lpShareMemory); 
  if (NULL != hFileMapping)    CloseHandle(hFileMapping); 
  if (NULL != hMutex)       ReleaseMutex(hMutex); 
  return 0; 
}


Related articles: