Linux C/C + + shared memory with message queue and semaphore encapsulation

Keywords: C++ Linux server multiple processes

Encapsulation of Linux 'C/C + + shared memory with message queue + semaphore

1, Train of thought

The server demo I do is divided into two servers: the front server and the rear server. The front server is used to receive information, contract and unpack, while the rear server is used to process the package business. The front and rear processes are divided into two processes to run, so the communication between processes under Linux is necessary for each other to receive the information sent to each other. My main packaging ideas for shared memory are as follows:

The A-end and B-end of the existing shared memory are the same shared memory, one write and one read

  1. Create a semaphore to realize the P V operation of readable resources and writable resources, and act as a buffer mechanism in shared memory reading and writing
  2. Create A message queue, and send messages to the message queue after the A-end shared memory is written; If the B end does not receive the message, it will block. After receiving the message, it is allowed to read data from the shared memory;
  3. Shared memory:
    i. A-end write will be blocked depending on the situation: for example, if the writable resources are full, the semaphore will block A-end write;
    II. There will be two blockages in the reading of end B as the case may be: for example, if the message sent by the message queue of end A is not received, the readable resource is zero, and the semaphore blocks the reading of end B;
  4. Overloads constructed:
    I. end a is the first creation end of shared memory, so it is necessary to provide the size, number and key value of shared memory blocks;
    II. If end B is not created for the first time, you only need to provide the key value when declaring the object;
  5. Memory structure of shared memory;

The picture is simple. Let's briefly introduce this picture:

The memory is divided into memory header + actual data area. The memory header is responsible for storing the given size (blocks*block_size) and the current readable subscript and writable subscript of the shared memory when creating the shared memory, and reading and writing the memory in the way of subscript available address offset

2, Three package source codes

  1. Message queue

    Class declaration

#pragma once

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <error.h>
#include <stdio.h>

// Note: the data type of the document message type is long. Be careful not to write it as int, otherwise the message may not be received
typedef struct basemsg {
	long mtype;       /* message type, must be > 0 */
	char mtext[1];    /* message data */
}BASEMSG,*LPMSGBUF;


class Msqque
{
public:
	Msqque(key_t& key);
	~Msqque();
	
	bool msg_send(LPMSGBUF &buff, int flag);
	bool msg_rcv(LPMSGBUF &buff, int flag);
	bool msg_del();
	
		
private:
	int msgid;


};


Class definition

#include "Msqque.h"
#include <iostream>

using namespace std;

Msqque::Msqque(key_t& key)
{
	
	msgid = msgget(key, IPC_CREAT | 0666);
	if (msgid == -1)
	{
		perror("msgget error:");
		//exit(0);
	}
	cout << "create message queue success,key = " << key << ";msgid = " << msgid << endl;
}


Msqque::~Msqque()
{
	this->msg_del();
}

bool Msqque::msg_send(LPMSGBUF &buff, int flag)
{
	int res = msgsnd(msgid, (void*)buff, sizeof(buff->mtext), flag);
	if (res < 0)
	{
		perror("msg send error:");
		return false;
		
	}
	cout << "send msg success,id = " << msgid << endl;
	return true;
	
}

bool Msqque:: msg_rcv(LPMSGBUF &buff, int flag)
{
    // When receiving a message, first define the message data type of buff. The message type needs to be specified directly through buff
	int res = msgrcv(msgid, (void*)buff, sizeof(buff->mtext), buff->mtype, flag);
	if (res < 0)
	{
		perror("msg rec error:");
		return false;
	}
	
	cout << "rcv msg success,id = " << msgid << endl;
		
	return true;
	
}


bool Msqque::msg_del() 
{
	int res = msgctl(msgid, IPC_RMID, NULL);
	if (res == -1)
	{
		perror("delete message error:");
		return false;
	}
	return true;
}
  1. Semaphore

    Class declaration

#pragma once

#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/sem.h>

union semun {
	int val;    /* Value for SETVAL */
	struct semid_ds* buf;    /* Buffer for IPC_STAT, IPC_SET */
	unsigned short* array;  /* Array for GETALL, SETALL */
	struct seminfo* __buf;  /* Buffer for IPC_INFO
								(Linux-specific) */
};


class CSem
{
public:
    // Similarly, overload construction is used to distinguish the creation and acquisition of semaphores
	CSem(key_t& key);
	CSem();
	~CSem();
	
public:
	int sem_p();									// p operation
	int sem_v();									// v operation
	void sem_del();									// Destroy semaphore
	int sem_open(key_t &key);						// Get existing semaphore
	int sem_setval(int val);						// Used to set the initial value
	int sem_getval();

	
private:
	int semid;
};

Class definition

#include "CSem.h"



#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>

using namespace std;


CSem::CSem()
{

}

CSem::CSem(key_t& key)
{
	int res = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
	cout << "semid = " << res << endl;

	if (res == -1)
	{
		perror("create sem error:");
		exit(-1);
	}
	semid = res;
}


CSem::~CSem()
{
	this->sem_del();
}


int CSem::sem_getval()
{
	int ret = semctl(semid, 0, GETVAL, 0);
	if (ret == -1)
	{
		perror("get sem value failure!");
		return -1;
	}
	return ret;
}

int CSem::sem_p()
{
	struct sembuf sops = { 0,-1,0 };
	int ret = semop(semid, &sops, 1);
	if (ret == -1)
	{
		perror("p operation failure!");
		return -1;
	}
	return ret;

}


int CSem::sem_v()
{
	struct sembuf sops = { 0,+1,0 };
	int ret = semop(semid, &sops, 1);
	if (ret == -1)
	{
		perror("v operation failure!");
		return -1;
	}
	return ret;
}



int CSem::sem_open(key_t& key)
{
	int ret = semget(key, 0, 0);		//Only the semaphore set id is obtained, and the 2 and 3 parameters do not need to be initialized
	if (ret == -1)
	{
		perror("create sem failure!");
		return -1;
	}
	semid = ret;
	return ret;
}


void CSem::sem_del()
{
	int res = semctl(semid, 0, IPC_RMID, 0);
	if (res == -1)
	{
		perror("delete sem error:");
		exit(-1);
	}
	
}


int CSem::sem_setval(int val)
{
	union semun su;
	su.val = val;
	int res = semctl(semid, 0, SETVAL, su);
	if (res == -1)
	{
		perror("sem set val error:");
		exit(-1);
	}
	return res;
}
  1. Shared memory

    Class declaration

    #pragma once
    #include <sys/shm.h>
    
    #include "CSem.h"
    #include "Msqque.h"
    typedef struct ShmHead
    {
    	int r_index;
    	int w_index;
    	int blocks;
    	int block_size;
    }ShmHead_t;
    
    class CShmFifo
    {
    public:
    	CShmFifo(key_t &key, int &blocks, int &block_size);
    	CShmFifo(key_t &key);
    	~CShmFifo();
    public:
    	void shm_del();					// Destroy shared memory
    	void write(char* buff);			// Write operation
    	void read(char* buff);			// Read operation
    
    
    public:
    	bool isDelSem;					// Destroy semaphore flag bit
    
    private:
    	int shmid;
    	ShmHead_t * shm_start_addr;		//Shared memory header address
    	char* shm_data_addr;			//First address of actual stored data
    	CSem *sem_p_id;					//Producer semaphore
    	CSem *sem_c_id;					//Consumer semaphore
    	Msqque *msgque;
    	LPMSGBUF temp;
    	
    };
    
    

    Class definition

    #include "CShm.h"
    #include <iostream>
    
    
    
    using namespace std;
    
    
    CShmFifo::CShmFifo(key_t& key, int& blocks, int& block_size)		//Create shared memory for the first time
    {
    	this->isDelSem = false;
    	temp = (LPMSGBUF)malloc(sizeof(BASEMSG));
    	memset(temp, '\0', sizeof(BASEMSG));
    	temp->mtype = (long)1;
    
    	cout << "Create shmfioing..." << endl;
    	this->shm_start_addr = (ShmHead_t*)malloc(sizeof(ShmHead_t));			
    	memset(this->shm_start_addr, '\0', sizeof(ShmHead_t));
    	int total_size = blocks * block_size;
    	shmid = shmget(key, total_size, IPC_CREAT | IPC_EXCL | 0666);
    	if (shmid == -1)	//Shared memory is not created for the first time. Exit the program
    	{
    		perror("shmfifo is exsits:");
    		exit(0);
    	}
    	//Create shared memory for the first time
    	this->msgque = new Msqque(key);
    
    	this->shm_start_addr = (ShmHead_t*)shmat(shmid, 0, 0);				//Mount shared memory
    	this->shm_start_addr->blocks = blocks;
    	this->shm_start_addr->block_size = block_size;
    	this->shm_start_addr->w_index = 0;
    	this->shm_start_addr->r_index = 0;
    	this->shm_data_addr = (char*)(this->shm_start_addr + 1);			//The first address is offset by a length of its type, and the result is the first address of the data store
    
    	//Create semaphore object
    	key_t key2 = key + 5;
    	
    	sem_p_id = new CSem(key);
    	sem_p_id->sem_setval(blocks);			//Initialize writable resources
    
    	sem_c_id = new CSem(key2);
    	sem_c_id->sem_setval(0);				//Initialize readable resources
    
    
    
    	cout << "First create shmfifo,shmid = " << shmid << endl;
    }
    
    CShmFifo::CShmFifo(key_t& key)
    {
    	this->isDelSem = false;
    	temp = (LPMSGBUF)malloc(sizeof(BASEMSG));
    	memset(temp, '\0', sizeof(BASEMSG));
    	temp->mtype = (long)1;
    
    	cout << "Create shmfioing..." << endl;
    	this->shm_start_addr = (ShmHead_t*)malloc(sizeof(ShmHead_t));
    	memset(this->shm_start_addr, '\0', sizeof(ShmHead_t));
    	shmid = shmget(key, 0, IPC_CREAT | 0666);
    	if (shmid == -1)
    	{
    		perror("create shmfifo second error:");
    		exit(-1);
    	}
    	this->msgque = new Msqque(key);						
    
    	// Get the first address of shared memory + the first address of actual stored data
    	this->shm_start_addr = (ShmHead_t*)shmat(shmid, 0, 0);
    	this->shm_data_addr = (char*)(this->shm_start_addr + 1);
    	
    	// Get semaphore
    	key_t key2 = key + 5;
    	this->sem_p_id = new CSem();
    	this->sem_p_id->sem_open(key);
    
    	this->sem_c_id = new CSem();
    	this->sem_c_id->sem_open(key2);
    	cout << "Craete shmfifo second success,shmid = " << shmid << endl;
    }
    
    
    void CShmFifo::write(char* buff)
    {
    	cout << "##     Write into shmfifo.......shmid = " << shmid << endl;
    	this->sem_p_id->sem_p();			//Writable resource-1
    
    	int write_offset = this->shm_start_addr->block_size * this->shm_start_addr->w_index;					//Offset to writeable first address
    	memcpy(this->shm_data_addr + write_offset, buff, this->shm_start_addr->block_size);						//Write a memory block
    	this->shm_start_addr->w_index = (this->shm_start_addr->w_index + 1) % this->shm_start_addr->blocks;		//Incremental readable subscript
    
    	this->sem_c_id->sem_v();			//Readable resources + 1
    	
    	cout << "##     Write into shmfifo success" << endl;
    
    	
    	this->msgque->msg_send(temp, 0);																	// Notify the right end of the shared memory connection to read resources
    	cout << "--------------------send message----------------" << endl;
    }
    
    void CShmFifo::read(char* buff)
    {
    	this->msgque->msg_rcv(temp, 0);																		//Wait for the left end of shared memory to notify to get readable resources
    	cout << "--------------------receive message----------------" << endl;
    	cout << "##     Read from shmfifo.......shmid =." << shmid << endl;
    	this->sem_c_id->sem_p();																			//Readable resource-1
    
    	int read_offset = this->shm_start_addr->block_size * this->shm_start_addr->r_index;					//Offset to readable first address
    	memcpy(buff, this->shm_data_addr + read_offset, this->shm_start_addr->block_size);
    	this->shm_start_addr->r_index = (this->shm_start_addr->r_index + 1) % this->shm_start_addr->blocks;
    
    	this->sem_p_id->sem_v();																			//Writable resource + 1
    	cout << "##     Read from shmfifo success" << endl;
    }
    
    
    
    CShmFifo::~CShmFifo()
    {
    	shm_del();
    	if (this->isDelSem == false)
    	{
    		sem_c_id->sem_del();
    		sem_p_id->sem_del();
    		msgque->msg_del();
    	}
    }
    
    
    void CShmFifo::shm_del()
    {
    	int res = shmctl(shmid, IPC_RMID, NULL);
    	if (res == -1)
    	{
    		perror("del shm error:");
    		exit(-1);
    	}
    }
    

3, Possible problems

  1. Message queue content message not found

    (1) the message structure definition type is wrong and should be long;

    (2) the message type parameter of the receiving message interface needs to be obtained from the message structure variable; (this is the summary of personal experience. If there is an error, please correct it.)

  2. Segment error

    (1) pointer variable has no space or is not initialized

    (2) the read / write subscript of shared memory is incorrect

    If the position of segment error cannot be determined by printing + break point debugging, g db debugging can be used to quickly locate the abnormal position

4, Reference blog

https://blog.csdn.net/ljianhui/article/details/10287879

https://www.cnblogs.com/wuyepeng/p/9748552.html

5, Welcome to correct

Posted by no_one on Fri, 08 Oct 2021 22:28:22 -0700