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
- 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
- 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;
- 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; - 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; - 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
-
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; }
-
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; }
-
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
-
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.)
-
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