What I'm going to talk about is not the message queue in IPC, I'm talking about implementing a custom message queue inside the process, allowing the messages of each thread to drive the movement of the entire process. Message Queuing between processes is used for communication between processes and processes, and the message queues that I will implement in-process are used to properly process requests from individual threads, avoiding swarm requests and causing unexpected loss of messages. Think of socket programming in the Listen function bar, the inside to set a queue length parameters, in fact, the request from the network has been queued to a request queue, but this queue is the system to help us do, we do not see it. If the system does not help us with this waiting queue, then we need the programmer to implement it in the application layer.
In-process Message Queuing implementation is not difficult, in general, the following points:
- Customize the message structure and construct the queue
- A thread is responsible for extracting the message from the message queue and processing the message in turn
- Multiple threads generate events and put messages into message queues waiting to be processed
Long story short, let's get started!
I. Define message structurePaste the code before explaining:
typedef struct msg_hdr_s { uint32 msg_type; UInt32 Msg_len; UInt32 msg_src; UInt32 msg_dst; } msg_hdr_t; typedef struct msg_s { msg_hdr_t Hdr; Uint8 data[100]; } msg_t;
Here is an explanation of the message format content I designed:
- Msg_type: Flags The message type, and when the message receiver sees the msg_type, he knows what he's going to do.
- Msg_len: Message length, to be extended, temporarily useless (later extended to a variable length message)
- MSG_SRC: The source address of the message, which is the initiator of the message
- MSG_DST: The destination of the message, which is the recipient of the message
- DATA[100]: Message drops the amount of information that can be carried outside the message header, defined as 100 bytes
By the message data structure can be known, this message is fixed length, of course, can also be implemented as a variable length message, but now do not implement, today, the fixed-length message is implemented, and then perfect the variable-length message.
Second, construct the cyclic queueA queue can be implemented by a linked list or by an array, where the loop linked list implemented by the array is used as the queue model for our message queue.
typedef structqueue_s {intHead IntRear sem_t sem; msg_t Data[queue_size]; }queue_t; int Msgqueueinit (queue_t*Q) {if (!Q) {printf ("Invalid queue!\n"); Return-1;} q->rear = 0 ; Q->head = 0 ; sem_init (&q->sem, 0, 1 ), return 0 ;} int Msgdequeue (queue_t* Q, msg_t* Msg) {if (! Q) {printf ("Invalid queue!\n" ); return-1 ;} if (q->rear = = q->head)//only One consumer,no need to lock Head {printf ("Empty queue!\n" ); return-1 ;} memcpy (MSG, & (Q->data[q->head]), sizeof (msg_t)); Q->head = (q->head+1)% queue_size; return 0 ;} int Msgenqueue (queue_t* Q, msg_t* Msg) {if (q->he AD = = (q->rear+1)% queue_size) {printf ("full queue!\n" ); return-1 ;} sem_wait (&q-> SEM); memcpy (& (Q->data[q->rear]), MSG, sizeof (msg_t)); Q->rear = (q->rear+1)% queue_size; Sem_post (&q-> sem); return 0 ;
The implementation of the cyclic queue must be familiar to everyone, but here are a few things to note:
- A semaphore or lock should be added to the queue to secure mutually exclusive access at the time of entry, as multiple messages may be queued at the same time, overwriting their queue nodes
- The semaphore here is only for the incoming team and not for the team, the reason is that there is only one message processor, there is no mutually exclusive case
Third, construct the message processor
if (pthread_create (&handler_thread_id, NULL, (void* ) Msg_handler, null)) {printf ("Create handler Threa D fail!\n "); Return-1 ; } void Msg_printer (msg_t* msg) {if (! msg) {return ;} printf ("%s:i has recieved a message!\n" , __function__); printf ("%s:msgtype:% D msg_src:%d dst:%d\n\n ", __function__,msg->hdr.msg_type,msg->hdr.msg_src,msg-> hdr.msg_dst); } void Msg_handler () {sleep (5);//let's wait 5s when starts while (1 ) {msg_t msg; memset (&msg, 0, sizeof< span> (msg_t)); int res = Msgdequeue ((queue_t*) &msgqueue, & msg), if (res! = 0 ) {sleep (); continue ;} msg _printer (& msg); Sleep (1 )}}
I create a thread in the process to handle Message Queuing messages as a message processor (handler), and when you enter the thread, wait 5 seconds for the producer to drop a message in the queue before starting the message processing. When the queue does not have the message to be desirable, rest for 10 seconds, and then fetch the message. The message processing here is simple, I simply print the message I received, proving that the message I received was Cheng to me by the other line. Of course, you can also extend the functionality here to further decide what to do depending on the type of message being received. Like what:
Enum msg_type { go_home, go_to_bed, go_to_lunch, Go_to_cinama, go_to_school, GO _dateing, GO_TO_WORK,//6 }; void Handler () { switch(msgtype) {case go_home:go_home (); Case go_to_bed: go_to_bed (), Break, ...}
Here the handler is a simple state machine, according to the given message type (event) to do specific things, to promote the rotation of the state machine.
Iv. Construction of Message producers
if (Pthread_create (&thread1_id, NULL, (void*) {msg_sender1, NULL) {printf ("Create Thread1 fail!\n")); Return-1; } if (Pthread_create (&thread2_id, NULL, (void*) {msg_sender2, NULL) {printf ("Create Thread2 fail!\n")); Return-1; } if (Pthread_create (&thread3_id, NULL, (void*) {Msg_sender3, NULL) {printf ("Create Thread3 fail!\n")); Return-1; } voidMsg_sender1 () {int i = 0; while (1) {if (i > 10) {i = 0; } msg_t MSG; Msg.hdr.msg_type = i++; MSG.HDR.MSG_SRC =THREAD1; MSG.HDR.MSG_DST =HANDLER; Msgenqueue (queue_t*) &msgqueue, & msg); printf ("%s:thread1 send a message!\n" , __function__), sleep (1 ),}} void msg_sender2 () {int i = 0 ; while (1 ) {if (i > ) {i = 0 ;} msg_t msg; Msg.hdr.msg_type = i++ ; msg.hdr.msg_src = THREAD2; msg.hdr.msg_dst = HANDLER; Msgenqueue ((queue_t*) &msgqueue, & msg); printf ("%s:thread2 send a message!\n" , __function__); Sleep (1 );}} void Msg_sender3 () {int i = 0 , while (1 ) {if (i > ) {i = 0 ;} msg_t msg; Msg.hdr.msg_type = i++ ; msg.hdr.msg_src = THREAD3; msg.hdr.msg_dst = HANDLER; Msgenqueue ((queue_t*) &msgqueue, & msg); printf ("%s:thread3 send a message!\n" , __function__); Sleep (1 );}
Here I create three threads to simulate message producers, each of which writes messages to the message queue every 1 seconds.
Five, run up and see
First put the complete code: MSG_QUEUE.C:
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <semaphore.h> 4 #include <unistd.h> ; 5 #include <string.h> 6 #include "msg_def.h" 7 8queue_t Msgqueue; 9 int main (int argc, char*Argv[]) 11{intRet 13pthread_t thread1_id; 14pthread_t thread2_id; 15pthread_t thread3_id; 16pthread_t handler_thread_id; + ret = msgqueueinit (queue_t*) &Msgqueue); if (ret! = 0) 20{return-1; 22} if (Pthread_create (&handler_thread_id, NULL, (void*) Msg_handler, NULL) 25{$ printf ("Create handler thread fail!\n"); Return-1; 28} if (Pthread_create (&thread1_id, NULL, (void*) Msg_sender1, NULL) 32{"Create Thread1 fail!\n"); Return-1; 35The PNs if (Pthread_create (&thread2_id, NULL, (void*) Msg_sender2, NULL) 38{thread2 printf ("Create fail!\n")); Return-1; 41} if (Pthread_create (&thread3_id, NULL, (void*) Msg_sender3, NULL) 44{printf ("Create Thread3 fail!\n")); Return-1; 47} (1) 51{% Sleep (1); 53} 0; 56}, Msgqueueinit Int (queue_t*Q) 62{if (!Q) 64{"Invalid queue!\n"); Return-1; 67} q->rear = 0; Q->head = 0; Sem_init (&q->sem, 0, 1); return 0; 72} msgdequeue Int (queue_t* Q, msg_t*MSG) 75{if (!Q) 77{queue!\n printf ("Invalid"); Return-1; 80} bayi if (q->rear = = q->head)//only One cosumer,no need to lock head 82{queue!\n printf ("Empty"); Return-1; 85} memcpy (MSG, & (Q->data[q->head)), sizeof(msg_t)); Q->head = (q->head+1)%Queue_size; return 0; 89 90} msgenqueue Int (queue_t* Q, msg_t*MSG) 93{94 if (Q->head = = (q->rear+1)%QUEUE_SIZE) 95{"Full queue!\n"); Return-1; 98} sem_wait (&Q->SEM); memcpy (& (Q->data[q->rear]), MSG, sizeof(msg_t)); 101 Q->rear = (q->rear+1)%Queue_size; 102 Sem_post (&Q->SEM); 103 return 0; 104} 106 void Msg_printer (msg_t*MSG) 107{108 if (!MSG) 109{return; 111}%s:i printf ("Recieved a message!\n", __function__); 113 printf ("%s:msgtype:%d msg_src:%d dst:%d\n\n",__function__,msg->hdr.msg_type,msg->hdr.msg_src,msg->HDR.MSG_DST); 114 115} 117 intMsg_send (118){119 120msg_t msg; 121 Msg.hdr.msg_type =Go_home; 122 MSG.HDR.MSG_SRC =THREAD1; 123 MSG.HDR.MSG_DST =HANDLER; 124 return Msgenqueue (queue_t*) &msgqueue, &msg); 125 126} 127-VoidMsg_handler (129){5);//let's wait 5s when starts 131 while (1) 132{133msg_t msg; 134 memset (&msg, 0, sizeof(msg_t)); 135 int res = Msgdequeue ((queue_t*) &msgqueue, &msg); 136 if (res! = 0) 137{138 Sleep (10); 139 Continue; 140} 141 Msg_printer (&msg); 142 Sleep (1); 143} 144} 145 146 147 voidMsg_sender1 (148){149 int i = 0; while (1) 151{if (i > 10) 153{154 i = 0; 155} 156msg_t msg; 157 Msg.hdr.msg_type = i++; 158 MSG.HDR.MSG_SRC =THREAD1; 159 MSG.HDR.MSG_DST =HANDLER; Msgenqueue (queue_t*) &msgqueue, &msg); 161 printf ("%s:thread1 Send a message!\n", __function__); 162 Sleep (1); 163} 164} 165 166 voidMsg_sender2 (167){168 int i = 0; 169 while (1) 170{171 if (i > 10) 172 {173 i = 0 ; 174 } 175 msg_t Msg; 176 msg.hdr.msg_type = i++ ; 177 msg.hdr.msg_src = THREAD2; 178 MSG.HDR.MSG_DST = HANDLER; 179 msgenqueue ((queue_t*) &msgqueue, & msg); Send a message!\n ", __function__); 181 Sleep (1 ), 182 } 183 } 184 185 void Msg_sender3 () 186 {187 int i = 0 ; 188 while (1 ) 189 {(i > ) 191 {192 i = 0 ; 193 } 194 msg_t Msg; 195 Msg.hdr. Msg_type = i++ ; 196 msg.hdr.msg_src = THREAD3; 197 msg.hdr.msg_dst = HANDLER; 198 msgenqueue ((queue_t*) & Amp Msgqueue, & msg); 199 printf ("%s:thread3 send a message!\n" , __function__); (1 ); 201 } 202}
Msg_def.h:
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <semaphore.h> 4 5 typedef unsigned CHARUint8; 6 typedef unsigned Shortunit16; 7 typedef unsigned INTUInt32; 8 9 #define QUEUE_SIZE typedef structmsg_hdr_s 12{13UInt32 Msg_type; 14UInt32 Msg_len; 15UInt32 msg_src; 16UInt32 MSG_DST; 17}msg_hdr_t; typedef structmsg_s 20{21msg_hdr_t Hdr; Uint8 data[100]; 23} msg_t; typedef structqueue_s 26{intHead int rear sem_t sem; msg_t data[queue_size]; }queue_t; typedef struct queue_s Queuenode; Enum msg_type {notoginseng go_home, Go_to_cinama go_to_bed, all go_to_lunch, + , Go_to_school go_dateing, Go_to_work,//6 }; enum src_addr + {
THREAD1, HANDLER
THREAD2, THREAD3, Msgqueueinit, (queue_t*) }; Q); msgdequeue Int (queue_t* q, msg_t* msg), Msgenqueue (queue_t* Q, msg_t* msg), + $ void Msg_handle R (); msg_sender2 void msg_sender1 (); void (); msg_t* void msg_sender3 (); msg_printer g); msg_send int ();
Look at the phenomenon of running: finish! now the architecture of the message queue in the process is very practical in practical engineering (of course, the framework of the actual project is much more complex and robust), and many projects need this event-driven idea to ensure that every request can be executed in a non-flocculation manner, so this framework is also useful , especially with the state machine is very suitable!
Custom Message Queuing for Linux programming