Shared memory is used between processes

1. Shared memory

Shared memory refers to opening up a space in the memory space for use by multiple processes. It is an important means of cross-process communication. Shared memory is widely used in multi-process development, especially when communicating large amounts of data across processes, it is almost a must-have choice. In engineering practice, it is used in the Android framework. Of course, many other well-known software have applications. If you are interested, you can search for it yourself.
The processing principle of shared memory in Linux and Windows is basically the same, but the method is different. This article only analyzes shared memory under Linux. Those who are interested in shared memory on Windows can find relevant information by themselves.
Under Linux, there are two ways to deal with shared memory: the traditional system V and Posix libraries.

2. Synchronization locks in use of shared memory in the process

In process communication, two problems are most often encountered, one is the read and write operation of data, and the other is the synchronization problem of reading and writing. The former is the basic function, here we focus on analyzing the synchronization problem. In actual engineering development, std::mutex is subconsciously selected in the lock selection of the process. This has a great influence on learning the famous Mutex in VC in the early years. With the evolution of technology in the later period, at the same time, there are very few actual projects for multi-process programming, which also leads to many problems. The basic framework is as follows:

struct SmData final {
  bool flag = false;
  std::mutex mt;
  std::array<int, 100> buffer = {0};
};

struct MemData final {
  // WorkID id;
  SmData buf[2] = {0};
};

The overall test process is good, but the lock is found to be invalid during the test. After the sending process of the two processes obtains the mt lock from the shared memory, the other receiving process can also obtain the lock from the shared memory. The test is like this many times, and the result obtained from the online query is that std::mutex cannot be used between processes. Take a look at the source code of its implementation:

  /// The standard mutex type.
  class mutex : private __mutex_base
  {
  public:
    typedef __native_type* 			native_handle_type;

#ifdef __GTHREAD_MUTEX_INIT
    constexpr
#endif
    mutex() noexcept = default;
    ~mutex() = default;

    mutex(const mutex&) = delete;
    mutex& operator=(const mutex&) = delete;
......
    native_handle_type
    native_handle() noexcept
    { return &_M_mutex; }
  };

No specific definition was found either. So I switched to pthread_mutex_t, and set the attribute to PTHREAD_PROCESS_SHARED (note that it can only be set in one process, here it is set in the writing process, and the other reading process can only operate), and the test is successful. Then repackage the related RAII class of pthread_mutex_t.
But still did not give up the test of std::mutex, adopted a more accurate test method, and found that the std::mutex of the standard library can also be used, which is really helpless. It should be that there is a problem with the previous test method, which caused a misunderstanding . If you change it, change it, if you don’t change it, go back.

For an example of POSIX shared memory, please refer to the following website:
https://www.bo-yang.net/2016/07/27/shared-memory-ring-buffer#shm_ring_buffer
It is very detailed, and the description is also in place, so I won’t repeat it here . It is only explained here that this method is similar to creating a memory map, and a file will be generated at the specified location of the application, and this file will increase with the increase of the open space, which is also a problem that needs attention. However, there is not much impact, after all, the size of the hard disk is not an order of magnitude compared to the memory.

Three, two routines:

The following are two repeatedly tested routines (including STL and Posix Mutex), which are a bit messy:
the following is to write shared memory:

//send
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <mutex>
namespace mem
{
template <typename Mutex_> class lock_guard final {
public:
	//lock_guard()=default;
  lock_guard(Mutex_ *mt) : mt_(mt) { pthread_mutex_lock(mt_); }
  ~lock_guard() { pthread_mutex_unlock(mt_); }

public:
  lock_guard(const lock_guard &) = delete;
  lock_guard(lock_guard &&) = delete;
  lock_guard &operator=(const lock_guard &) = delete;
  lock_guard &operator=(lock_guard &&) = delete;

private:
  Mutex_ *mt_;
};
}
struct LockTest
{
int d;
std::mutex mt;
//pthread_mutex_t mt;
char buf[1024];
};
struct LockArr
{
LockTest lt[6];
};
LockArr *tmp = NULL;
std::mutex* init_shm(const key_t key)
{
    int shmid = shmget(key, sizeof(LockArr), 0666 | IPC_CREAT);
    if (shmid == -1)
    {
        return NULL;
    }

    void* shm_addr = shmat(shmid, NULL, 0);
    if (shm_addr == (void *) -1)
    {
        return NULL;
    }
 
    //pthread_mutexattr_t mutex_attr;
    //pthread_mutexattr_init(      &mutex_attr);
    //pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
    //pthread_mutexattr_setrobust( &mutex_attr, PTHREAD_MUTEX_ROBUST);

    tmp = (LockArr *)shm_addr;
    std::mutex* _mutex = &tmp->lt[0].mt;
    //pthread_mutex_t* _mutex = &tmp->lt[0].mt;
    //pthread_mutex_init(_mutex,&mutex_attr);

    return _mutex;
}
#define PKEY 20181220
//process
std::mutex* pMutex = NULL;
void writemem(const unsigned char* data)
{
    if (pMutex == NULL)
    {
        pMutex = init_shm(PKEY);
    }

    std::lock_guard<std::mutex> l(*pMutex);
    //mem::lock_guard<pthread_mutex_t> pmt{pMutex};
    //pthread_mutex_lock(pMutex);

    //do something
    tmp->lt[0].buf[0] = 1;
    tmp->lt[0].buf[1] = 3;
    tmp->lt[0].buf[2] = 6;
    printf("lock successed--------------ooooooo--------------\n");
    usleep(10 * 1000 * 1000);


    //pthread_mutex_unlock(pMutex);
}
int main()
{
	unsigned char buf[1024] = {0};
        writemem(buf);
	return 0;
}

The following is to read shared memory:

//recv
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
#include <mutex>
namespace mem{
template <typename Mutex_> class lock_guard final {
public:
	//lock_guard()=default;
  lock_guard(Mutex_ *mt) : mt_(mt) { pthread_mutex_lock(mt_); }
  ~lock_guard() { pthread_mutex_unlock(mt_); }

public:
  lock_guard(const lock_guard &) = delete;
  lock_guard(lock_guard &&) = delete;
  lock_guard &operator=(const lock_guard &) = delete;
  lock_guard &operator=(lock_guard &&) = delete;

private:
  Mutex_ *mt_;
};
}
struct LockTest
{
int d;
std::mutex mt;
//pthread_mutex_t mt;
char buf[1024];
};
struct LockArr
{
LockTest lt[6];
};
LockArr *tmp = NULL;

/*pthread_mutex_t* */ std::mutex *init_shm(const key_t  key)
{
    int shmid = shmget( key, sizeof(LockArr), 0666 | IPC_CREAT);
    if (shmid == -1)
    {
        return NULL;
    }

    void* shm_addr = shmat(shmid, NULL, 0);
    if (shm_addr == (void *) -1)
    {
        return NULL;
    }
 
    //pthread_mutexattr_t mutex_attr;
    //pthread_mutexattr_init(      &mutex_attr);
    //pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
    //pthread_mutexattr_setrobust( &mutex_attr, PTHREAD_MUTEX_ROBUST);

    tmp = (LockArr *)shm_addr;
    //pthread_mutex_t* _mutex  = &tmp->lt[0].mt;
    std::mutex* _mutex  = &tmp->lt[0].mt;
    //pthread_mutex_init(_mutex,&mutex_attr);

    return _mutex;
}
#define PKEY 20181220
//process
std::mutex* pMutex = NULL;
void readmem(const unsigned char* data)
{
    if (pMutex == NULL)
    {
        pMutex = init_shm(PKEY);
    }
    //lock_guard<pthread_mutex_t>(&pBuf->mt)
    //mem::lock_guard<pthread_mutex_t> pmt(pMutex);
    std::lock_guard<std::mutex> l(*pMutex);
    //pthread_mutex_lock(pMutex);

    //do something
    std::cout<<tmp->lt[0].buf[0]<<tmp->lt[0].buf[1]<<tmp->lt[0].buf[2]<<std::endl;

    printf("lock successed=======99999================\n");
    usleep(3 * 1000 * 1000);


    //pthread_mutex_unlock(pMutex);
}
int main()
{
	unsigned char buf[1024] = {0};
        readmem(buf);
	return 0;
}

At present, this is the std::mutex of the standard library. If you release pthread_mutex_t and comment out the standard library, there is no problem, but you have to encapsulate a lock to control the mutex yourself.
Here is a question by the way, look at the following code:

//lock_guard<pthread_mutex_t>(&pBuf->mt);//这行代码OK
//lock_guard<pthread_mutex_t>(pMutex);//编译有问题
//lock_guard()=default;

The above two lines of code are commented, and the second line of code introduces the appearance of the third line of code below (although it can be compiled after adding the third line of code), but they are problematic. Error, because formally speaking the compiler thinks it is correct, but it does not actually achieve the purpose. The errors and warnings reported by the compiler are:

error:candidate expects 1 argument, 0 provided
warning:parentheses were disambiguated as redundant parentheses around declaration of variable named

This leads to a basic problem. When declaring an anonymous class object, if you use a specific value to define a class, there is no problem. If you use a variable with the same value to define it, the compiler cannot determine what is going on here. (Is it a class object definition or a function declaration), so that the compilation error warns (if the variable or function in the subsequent cascade call object is also fine, the compiler can determine that it is a class object definition). The solution is to either directly adopt the curly braces under the new standard, namely lock_guard<pthread_mutex_t>{pMutex}, or add a variable definition as shown in the code in the text as normal development.
There are also two closing functions in shared memory:

//共享内存从当前进程中分离,相当于shmat函数的反操作
int shmdt(const void *shmaddr);
//删除共享内存,这个其实有很多种用法,看command的标志
int shmctl(int shm_id, int command, struct shmid_ds *buf);

For more details, please refer to the relevant information.

Four. Summary

Under the new C++ standard, many new locks are provided, such as scoped_lock, shared_lock (corresponding mutexes: timed_mutex, recursive_mutex, recursive_timed_mutex, shared_mutex, shared_timed_mutex). It is still necessary to take a good look at the application of related locks and the use of cross-processes. Without practice, there is no right to speak.

Guess you like

Origin blog.csdn.net/fpcc/article/details/131825462