1@page page_thread_comm Inter-thread Communication 2 3In the last chapter, we talked about inter-thread synchronization, concepts such as semaphores, mutexes, and event sets were mentioned. Following the last chapter, this chapter is going to explain inter-thread communication. In bare-metal programming, global variables are often used for communication between functions. For example, some functions may change the value of a global variable due to some operations. Another function reads the global variable and will perform corresponding actions to achieve communication and collaboration according to the global variable values it read. More tools are available in RT-Thread to help pass information between different threads. These tools are covered in more detail in this chapter. After reading this chapter, you will learn how to use mailboxes, message queues, and signals for communication between threads. 4 5# Mailbox 6 7Mailbox service is a typical inter-thread communication method in real-time operating systems.For example, there are two threads, thread 1 detects the state of the button and sends it's state, and thread 2 reads the state of the button and turns on or off the LED according to the state of the button. Here, a mailbox can be used to communicate. Thread 1 sends the status of the button as an email to the mailbox. Thread 2 reads the message in the mailbox to get the button status and turn on or off the LED accordingly. 8 9Thread 1 here can also be extended to multiple threads. For example, there are three threads, thread 1 detects and sends the button state, thread 2 detects and sends the ADC information, and thread 3 performs different operations according to the type of information received. 10 11## Mailbox Working Mechanism 12 13The mailbox of the RT-Thread operating system is used for inter-thread communication, which is characterized by low overhead and high efficiency. Each message in the mailbox can only hold a fixed 4 bytes(for a 32-bit processing system, the pointer is 4 bytes in size, so an email can hold only one pointer). A typical mailbox is also called message exchange. As shown in the following figure, a thread or an interrupt service routine sends a 4-byte message to a mailbox, and one or more threads can receive and process the message from the mailbox. 14 15 16 17The sending process of the non-blocking mode mails can be safely used in ISR. It is an effective way for the thread, the interrupt service, and the timer to send a message to the thread. In general, the receiving of the mails can be a blocked process, depending on whether there is a message in the mailbox and the timeout set when the message was received. When there is no mail in the mailbox and the set timeout is not 0, the receive of the mails will become blocked. In such cases, the mails can only be received by threads. 18 19When a thread sends a message to a mailbox, if the mailbox is not full, the message will be copied to the mailbox. If the mailbox is full, the thread sending the message can set a timeout, and choose to wait and suspend or return directly - RT_EFULL. If the thread sending the message chooses to suspend and wait, then when the mails in the mailbox are received and space is left open again, the thread sending the message will be awaken and will continue to send. 20 21When a thread receives a message from a mailbox, if the mailbox is empty, the thread receiving the message can choose whether to set a timeout or wait and suspend until a new message is received to be awaken. When the set timeout is up and the mailbox still hasn't received the message, the thread that chose to wait till timeout will be awaken and return -RT_ETIMEOUT. If there are messages in the mailbox, then the thread receiving the message will copy the 4-byte message in the mailbox to the receiving cache. 22 23## Mailbox Control Block 24 25In RT-Thread, the mailbox control block is a data structure used by the operating system to manage mailboxes, represented by the structure `struct rt_mailbox`. Another C expression, `rt_mailbox_t`, represents the handle of the mailbox, and the implementation in C language is a pointer to the mailbox control block. See the following code for a detailed definition of the mailbox control block structure: 26 27 28```c 29struct rt_mailbox 30{ 31 struct rt_ipc_object parent; 32 33 rt_uint32_t* msg_pool; /* the start address of the mailbox buffer */ 34 rt_uint16_t size; /* the size of the mailbox buffer */ 35 36 rt_uint16_t entry; /* the number of messages in the mailbox */ 37 rt_uint16_t in_offset, out_offset; /* the entry and exit pointer of the mailbox buffer */ 38 rt_list_t suspend_sender_thread; /* send the suspend and wait queue of the thread */ 39}; 40typedef struct rt_mailbox* rt_mailbox_t; 41``` 42 43The `rt_mailbox` object is derived from `rt_ipc_object` and is managed by the IPC container. 44 45## Management of Mailbox 46 47The mailbox control block is a structure that contains important parameters related to mailbox and it plays an important role in the function implementation of the mailbox. The relevant interfaces of the mailbox are as shown in the following figure. The operation on a mailbox includes: create/initiate a mailbox, send a mail, receive a mail, and delete/detach a mailbox. 48 49 50 51### Create and Delete Mailbox 52 53To dynamically create a mailbox object, call the following function interface: 54 55```c 56rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag); 57``` 58 59When a mailbox object is created, a mailbox object is first allocated from the object manager, and then a memory space is dynamically allocated to the mailbox for storing the mail. The size of the memory is equal to the product of the message size (4 bytes) and the mailbox capacity. Then initialize the number of incoming messages and the offset of the outgoing message in the mailbox. The following table describes the input parameters and return values for this function: 60 61Input parameters and return values of rt_mb_create() 62 63|**Parameters** |**Description** | 64|----------------|------------------------------------------------------------------| 65| name | The name of the mailbox | 66| size | Mailbox capacity | 67| flag | The mailbox flag, which can take the following values: RT_IPC_FLAG_FIFO or RT_IPC_FLAG_PRIO | 68|**Return** | —— | 69| RT_NULL | Creation failed | 70| The handle of the mailbox object | Creation successful | 71 72When a mailbox created with rt_mb_create() is no longer used, it should be deleted to release the corresponding system resources. Once the operation is completed, the mailbox will be permanently deleted. The function interface for deleting a mailbox is as follows: 73 74```c 75rt_err_t rt_mb_delete (rt_mailbox_t mb); 76``` 77 78When deleting a mailbox, if a thread is suspended on the mailbox object, the kernel first wakes up all threads suspended on the mailbox (the thread return value is -RT_ERROR), then releases the memory used by the mailbox, and finally deletes the mailbox object. The following table describes the input parameters and return values for this function: 79 80Input parameters and return values of rt_mb_delete() 81 82|**Parameters**|**Description** | 83|----------|----------------| 84| mb | The handle of the mailbox object | 85|**Return**| —— | 86| RT_EOK | Successful | 87 88### Initialize and Detach Mailbox 89 90Initializing a mailbox is similar to creating a mailbox, except that the mailbox initialized is for static mailbox objects. Different from creating a mailbox, the memory of a static mailbox object is allocated by the compiler during system compilation which is usually placed in a read-write data segment or an uninitialized data segment. The rest of the initialization is the same as the creation of a mailbox. The function interface is as follows: 91 92```c 93 rt_err_t rt_mb_init(rt_mailbox_t mb, 94 const char* name, 95 void* msgpool, 96 rt_size_t size, 97 rt_uint8_t flag) 98``` 99 100When the mailbox is initialized, the function interface needs to obtain the mailbox object control block that the user has applied for, the pointer of the buffer, the mailbox name and mailbox capacity (the number of messages that can be stored). The following table describes the input parameters and return values for this function: 101 102Input parameters and return values of rt_mb_init() 103 104|**Parameters**|**Description** | 105|----------|-----------------------------------------------------------------| 106| mb | The handle of the mailbox object | 107| name | Mailbox name | 108| msgpool | Buffer pointer | 109| size | Mailbox capacity | 110| flag | The mailbox flag, which can take the following values: RT_IPC_FLAG_FIFO or RT_IPC_FLAG_PRIO | 111|**Return**| —— | 112| RT_EOK | Successful | 113 114The size parameter here specifies the capacity of the mailbox, if the number of bytes in the buffer pointed to by msgpool is N, then the mailbox capacity should be N/4. 115 116Detaching the mailbox means to detach the statically initialized mailbox objects from the kernel object manager. Use the following interface to detach the mailbox: 117 118```c 119rt_err_t rt_mb_detach(rt_mailbox_t mb); 120``` 121 122After using this function interface, the kernel wakes up all the threads suspended on the mailbox (the threads return -RT_ERROR), and then detaches the mailbox objects from the kernel object manager. The following table describes the input parameters and return values for this function: 123 124Input parameters and return values for rt_mb_detach() 125 126|**Parameters**|**Description** | 127|----------|----------------| 128| mb | The handle of the mailbox object | 129|**Return**| —— | 130| RT_EOK | Successful | 131 132### Send Mail 133 134The thread or ISR can send mail to other threads through the mailbox. The function interface of sending mails is as follows: 135 136```c 137rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value); 138``` 139 140The message sent can be any data 32-bit formatted, an integer value or a pointer pointing to the buffer. When the mailbox is fully filled with mails, the thread or ISR that sent the mail will receive a return value of -RT_EFULL. The following table describes the input parameters and return values for this function: 141 142Input parameters and return values of rt_mb_send() 143 144|**Parameters** |**Description** | 145|------------|----------------| 146| mb | The handle of the mailbox object | 147| value | Content of email | 148|**Return** | —— | 149| RT_EOK | Sent successfully | 150| \-RT_EFULL | The mailbox is filled | 151 152### Send Mails with Waiting 153 154Users can also send mails to specified mailbox through the following function interface: 155 156```c 157rt_err_t rt_mb_send_wait (rt_mailbox_t mb, 158 rt_uint32_t value, 159 rt_int32_t timeout); 160``` 161 162The difference between rt_mb_send_wait() and rt_mb_send() is that there is waiting time. If the mailbox is full, the thread sending the message will wait for the mailbox to release space as mails are received according to the set timeout parameter. If by the set timeout, there is still no available space, then the thread sending the message will wake up and return an error code. The following table describes the input parameters and return values for this function: 163 164Input parameters and return values of rt_mb_send_wait() 165 166|**Parameters** |**Description** | 167|---------------|----------------| 168| mb | The handle of the mailbox object | 169| value | Mail content | 170| timeout | Timeout | 171|**Return** | —— | 172| RT_EOK | Sent successfully | 173| \-RT_ETIMEOUT | Timeout | 174| \-RT_ERROR | Failed, return error | 175 176### Receive Mails 177 178Only when there is a mail in the mailbox, the recipient can receive the mail immediately and return RT_EOK. Otherwise, the thread receiving the message will suspend on the waiting thread queue of the mailbox or return directly according to the set timeout. The receiving mail function interface is as follows: 179 180```c 181rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout); 182``` 183 184When receiving a mail, the recipient needs to specify the mailbox handle, and specify the location to store the received message and the maximum timeout that it can wait. If a timeout is set at the time of receiving, -RT_ETIMEOUT will be returned when the message has not been received within the specified time. The following table describes the input parameters and return values for this function: 185 186Input parameters and return values of rt_mb_recv() 187 188|**Parameters** |**Description** | 189|---------------|----------------| 190| mb | The handle of the mailbox object | 191| value | Mail content | 192| timeout | Timeout | 193|**Return** | —— | 194| RT_EOK | Sent successfully | 195| \-RT_ETIMEOUT | Timeout | 196| \-RT_ERROR | Failed, return error | 197 198## Mail Usage Sample 199 200This is a mailbox application routine that initializes 2 static threads, 1 static mailbox object, one of the threads sends mail to the mailbox, and one thread receives mail from the mailbox. As shown in the following code: 201 202 Mailbox usage routine 203 204```c 205#include <rtthread.h> 206 207#define THREAD_PRIORITY 10 208#define THREAD_TIMESLICE 5 209 210/* Mailbox control block */ 211static struct rt_mailbox mb; 212/* Memory pool for mails storage */ 213static char mb_pool[128]; 214 215static char mb_str1[] = "I'm a mail!"; 216static char mb_str2[] = "this is another mail!"; 217static char mb_str3[] = "over"; 218 219ALIGN(RT_ALIGN_SIZE) 220static char thread1_stack[1024]; 221static struct rt_thread thread1; 222 223/* Thread 1 entry */ 224static void thread1_entry(void *parameter) 225{ 226 char *str; 227 228 while (1) 229 { 230 rt_kprintf("thread1: try to recv a mail\n"); 231 232 /* Receive mail from the mailbox */ 233 if (rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER) == RT_EOK) 234 { 235 rt_kprintf("thread1: get a mail from mailbox, the content:%s\n", str); 236 if(!strcmp(str, mb_str3)) 237 break; 238 239 /* Delay 100ms */ 240 rt_thread_mdelay(100); 241 } 242 } 243 /* Executing the mailbox object detachment */ 244 rt_mb_detach(&mb); 245} 246 247ALIGN(RT_ALIGN_SIZE) 248static char thread2_stack[1024]; 249static struct rt_thread thread2; 250 251/* Thread 2 entry*/ 252static void thread2_entry(void *parameter) 253{ 254 rt_uint8_t count; 255 256 count = 0; 257 while (count < 10) 258 { 259 count ++; 260 if (count & 0x1) 261 { 262 /* Send the mb_str1 address to the mailbox */ 263 rt_mb_send(&mb, (rt_uint32_t)&mb_str1); 264 } 265 else 266 { 267 /* Send the mb_str2 address to the mailbox */ 268 rt_mb_send(&mb, (rt_uint32_t)&mb_str2); 269 } 270 271 /* Delay 200ms */ 272 rt_thread_mdelay(200); 273 } 274 275 /* Send mail to inform thread 1 that thread 2 has finished running */ 276 rt_mb_send(&mb, (rt_uint32_t)&mb_str3); 277} 278 279int mailbox_sample(void) 280{ 281 rt_err_t result; 282 283 /* Initialize a mailbox */ 284 result = rt_mb_init(&mb, 285 "mbt", /* Name is mbt */ 286 &mb_pool[0], /* The memory pool used by the mailbox is mb_pool */ 287 sizeof(mb_pool) / 4, /* The number of messages in the mailbox because a message occupies 4 bytes */ 288 RT_IPC_FLAG_FIFO); /* Thread waiting in FIFO approach */ 289 if (result != RT_EOK) 290 { 291 rt_kprintf("init mailbox failed.\n"); 292 return -1; 293 } 294 295 rt_thread_init(&thread1, 296 "thread1", 297 thread1_entry, 298 RT_NULL, 299 &thread1_stack[0], 300 sizeof(thread1_stack), 301 THREAD_PRIORITY, THREAD_TIMESLICE); 302 rt_thread_startup(&thread1); 303 304 rt_thread_init(&thread2, 305 "thread2", 306 thread2_entry, 307 RT_NULL, 308 &thread2_stack[0], 309 sizeof(thread2_stack), 310 THREAD_PRIORITY, THREAD_TIMESLICE); 311 rt_thread_startup(&thread2); 312 return 0; 313} 314 315/* Export to the msh command list */ 316MSH_CMD_EXPORT(mailbox_sample, mailbox sample); 317``` 318 319The simulation results are as follows: 320 321``` 322 \ | / 323- RT - Thread Operating System 324 / | \ 3.1.0 build Aug 27 2018 325 2006 - 2018 Copyright by rt-thread team 326msh >mailbox_sample 327thread1: try to recv a mail 328thread1: get a mail from mailbox, the content:I'm a mail! 329msh >thread1: try to recv a mail 330thread1: get a mail from mailbox, the content:this is another mail! 331… 332thread1: try to recv a mail 333thread1: get a mail from mailbox, the content:this is another mail! 334thread1: try to recv a mail 335thread1: get a mail from mailbox, the content:over 336``` 337 338The routine demonstrates how to use the mailbox. Thread 2 sends the mails, for a total of 11 times; thread 1 receives the mails, for a total of 11 mails, prints the contents of the mail, and determines end. 339 340## Occasions to Use Mails 341 342Mailbox is a simple inter-thread messaging method, which is characterized by low overhead and high efficiency. In the implementation of the RT-Thread operating system, a 4-byte message can be delivered at a time, and the mailbox has certain storage capabilities, which can cache a certain number of messages (the number of messages is determined by the capacity specified when creating and initializing the mailbox). The maximum length of a message in a mailbox is 4 bytes, so the mailbox can be used for messages less than 4 bytes. Since on the 32 system, 4 bytes can be placed right on a pointer, when a larger message needs to be transferred between threads, a pointer pointing to a buffer can be sent as an mail to the mailbox, which means the mailbox can also delivery a pointer. For example, 343 344```c 345struct msg 346{ 347 rt_uint8_t *data_ptr; 348 rt_uint32_t data_size; 349}; 350``` 351 352For such a message structure, it contains a pointer pointing to data `data_ptr` and a variable `data_size` of the length of the data block. When a thread needs to send this message to another thread, the following operations can be used: 353 354```c 355struct msg* msg_ptr; 356 357msg_ptr = (struct msg*)rt_malloc(sizeof(struct msg)); 358msg_ptr->data_ptr = ...; /* Point to the corresponding data block address */ 359msg_ptr->data_size = len; /* Length of data block */ 360/* Send this message pointer to the mb mailbox */ 361rt_mb_send(mb, (rt_uint32_t)msg_ptr); 362``` 363 364When receiving the thread, because the pointer is what's being received, and msg_ptr is a newly allocated memory block, so after the thread receiving the message finishes processing, the corresponding memory block needs to be released: 365 366```c 367struct msg* msg_ptr; 368if (rt_mb_recv(mb, (rt_uint32_t*)&msg_ptr) == RT_EOK) 369{ 370 /* After the thread receiving the message finishes processing, the corresponding memory block needs to be released: */ 371 rt_free(msg_ptr); 372} 373``` 374 375# Message Queue 376 377Message Queuing is another commonly used inter-thread communication method, which is an extension of the mailbox. Can be used in a variety of occasions, like message exchange between threads, use the serial port to receive variable length data, etc. 378 379## Message Queue Working Mechanism 380 381Message queue can receive messages with unfixed length from threads or ISR and cache messages in their own memory space. Other threads can also read the corresponding message from the message queue, and when the message queue is empty, the thread reading the messages can be suspended. When a new message arrives, the suspended thread will be awaken to receive and process the message. Message queue is an asynchronous way of communication. 382 383As shown in the following figure, a thread or interrupt service routine can put one or more messages into the message queue. Similarly, one or more threads can also get messages from the message queue. When multiple messages are sent to the message queue, the message that first enters the message queue is first passed to the thread. That is, the thread first gets the message that first enters the message queue, in other words, it follows the first in first out principle (FIFO). 384 385 386 387The message queue object of the RT-Thread operating system consists of multiple elements. When a message queue is created, it is assigned a message queue control block: name, memory buffer, message size, and queue length. At the same time, each message queue object contains multiple message boxes, and each message box can store one message. The first and last message boxes in the message queue are respectively called the message linked list header and the message linked list tail, corresponding to `msg_queue_head` and `msg_queue_tail` in the queue control block. Some message boxes may be empty, they form a linked list of idle message boxes via `msg_queue_free`. The total number of message boxes in all message queues is the length of the message queue, which can be specified when creating the message queue. 388 389## Message Queue Control Block 390 391In RT-Thread, a message queue control block is a data structure used by the operating system to manage message queues, represented by the structure `struct rt_messagequeue`. Another C expression, `rt_mq_t`, represents the handle of the message queue. The implementation in C language is a pointer to the message queue control block. See the following code for a detailed definition of the message queue control block structure: 392 393```c 394struct rt_messagequeue 395{ 396 struct rt_ipc_object parent; 397 398 void* msg_pool; /* Pointer pointing to the buffer storing the messages */ 399 400 rt_uint16_t msg_size; /* The length of each message */ 401 rt_uint16_t max_msgs; /* Maximum number of messages that can be stored */ 402 403 rt_uint16_t entry; /* Number of messages already in the queue */ 404 405 void* msg_queue_head; /* Message linked list header */ 406 void* msg_queue_tail; /* Message linked list tail */ 407 void* msg_queue_free; /* Idle message linked list */ 408}; 409typedef struct rt_messagequeue* rt_mq_t; 410``` 411 412rt_messagequeue object is derived from rt_ipc_object and is managed by the IPC container. 413 414## Management of Message Queue 415 416The message queue control block is a structure that contains important parameters related to the message queue and plays an important role in the implementation of functions of the message queue. The relevant interfaces of the message queue are shown in the figure below. Operations on a message queue include: create a message queue - send a message - receive a message - delete a message queue. 417 418 419 420### Create and Delete Message Queue 421 422The message queue should be created before it is used, or the existing static message queue objects should be initialized. The function interface for creating the message queue is as follows: 423 424```c 425rt_mq_t rt_mq_create(const char* name, rt_size_t msg_size, 426 rt_size_t max_msgs, rt_uint8_t flag); 427``` 428 429When creating a message queue, first allocate a message queue object from the object manager, then allocate a memory space to the message queue object forming an idle message linked list. The size of this memory = [message size + message header (for linked list connection) Size] X the number of messages in the message queue. Then initialize the message queue, at which point the message queue is empty. The following table describes the input parameters and return values for this function: 430 431 Input parameters and return values for rt_mq_create() 432 433|**Parameters** |**Description** | 434|--------------------|-------------------------------------------------------------------------------| 435| name | The name of the message queue | 436| msg_size | The maximum length of a message in the message queue, in bytes | 437| max_msgs | The number of messages in the message queue | 438| flag | The waiting method took by the message queue, which can take the following values: RT_IPC_FLAG_FIFO or RT_IPC_FLAG_PRIO | 439|**Return** | —— | 440| RT_EOK | Sent successfully | 441| The handle of the message queue object | Successful | 442| RT_NULL | Fail | 443 444When the message queue is no longer in use, it should be deleted to free up system resources, and once the operation is complete, the message queue will be permanently deleted. The function interface for deleting the message queue is as follows: 445 446```c 447rt_err_t rt_mq_delete(rt_mq_t mq); 448``` 449 450When deleting a message queue, if a thread is suspended on the message queue's waiting queue, the kernel wakes up all the threads suspended on the message waiting queue (the thread returns the value is -RT_ERROR), and then releases the memory used by the message queue. Finally, delete the message queue object. The following table describes the input parameters and return values for this function: 451 452Input parameters and return values of rt_mq_delete() 453 454|**Parameters**|**Description** | 455|----------|--------------------| 456| mq | The handle of the message queue object | 457|**Return**| —— | 458| RT_EOK | Successful | 459 460### Initialize and Detach Message Queue 461 462Initializing a static message queue object is similar to creating a message queue object, except that the memory of a static message queue object is allocated by the compiler during system compilation and is typically placed in a read data segment or an uninitialized data segment. Initialization is required before using such static message queue objects. The function interface for initializing a message queue object is as follows: 463 464```c 465rt_err_t rt_mq_init(rt_mq_t mq, const char* name, 466 void *msgpool, rt_size_t msg_size, 467 rt_size_t pool_size, rt_uint8_t flag); 468``` 469 470When the message queue is initialized, the interface requires the handle to the message queue object that the user has requested (that is, a pointer pointing to the message queue object control block), the message queue name, the message buffer pointer, the message size, and the message queue buffer size. As shown in the following figure, all messages after the message queue is initialized are suspended on the idle message list, and the message queue is empty. The following table describes the input parameters and return values for this function: 471 472Input parameters and return values of rt_mq_init() 473 474|**Parameters** |**Description** | 475|-----------|-------------------------------------------------------------------------------| 476| mq | The handle of the message queue object | 477| name | The name of the message queue | 478| msgpool | Pointer pointing to the buffer storing the messages | 479| msg_size | The maximum length of a message in the message queue, in bytes | 480| pool_size | The buffer size for storing messages | 481| flag | The waiting method took by the message queue, which can take the following values: RT_IPC_FLAG_FIFO or RT_IPC_FLAG_PRIO | 482|**Return** | —— | 483| RT_EOK | Successful | 484 485Detaching the message queue will cause the message queue object to be detached from the kernel object manager. The following interface is used to detach from the message queue: 486 487```c 488rt_err_t rt_mq_detach(rt_mq_t mq); 489``` 490 491After using this function interface, the kernel wakes up all threads suspended on the message waiting queue object (the thread return value is -RT_ERROR) and then detaches the message queue object from the kernel object manager. The following table describes the input parameters and return values for this function: 492 493Input parameters and return values for rt_mq_detach() 494 495|**Parameters**|**Description** | 496|----------|--------------------| 497| mq | The handle of the message queue object | 498|**Return**| —— | 499| RT_EOK | Successful | 500 501### Send Message 502 503A thread or ISR can send a message to the message queue. When sending a message, the message queue object first takes an idle message block from the idle message list, then copies the content of the message sent by the thread or the interrupt service program to the message block, and then suspends the message block to the end of the message queue. The sender can successfully send a message if and only if there is an idle message block available on the idle message list; when there is no message block available on the idle message list, it means that the message queue is full, at this time, the thread or interrupt program that sent the message will receive an error code (-RT_EFULL). The function interface for sending messages is as follows: 504 505```c 506rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size); 507``` 508 509When sending a message, the sender specifies the object handle of the sent message queue (that is, a pointer pointing to the message queue control block) and specifies the content of the message being sent and the size of the message. As shown in the figure below, after sending a normal message, the first message on the idle message list is transferred to the end of the message queue. The following table describes the input parameters and return values for this function: 510 511Input parameters and return values of rt_mq_send() 512 513|**Parameter** |**Description** | 514|------------|------------------------------------------------------| 515| mq | The handle of the message queue object | 516| buffer | Message content | 517| size | Message size | 518|**Return** | —— | 519| RT_EOK | Successful | 520| \-RT_EFULL | Message queue is full | 521| \-RT_ERROR | Failed, indicating that the length of the sent message is greater than the maximum length of the message in the message queue. | 522 523### Send an Emergency Message 524 525The process of sending an emergency message is almost the same as sending a message. The only difference is that when an emergency message is sent, the message block taken from the idle message list is not put in the end of the message queue, but the head of the queue. The recipient can receive the emergency message preferentially, so that the message can be processed in time. The function interface for sending an emergency message is as follows: 526 527```c 528rt_err_t rt_mq_urgent(rt_mq_t mq, void* buffer, rt_size_t size); 529``` 530 531The following table describes the input parameters and return values for this function: 532 533Input parameters and return values of rt_mq_urgent() 534 535|**Parameters** |**Description** | 536|------------|--------------------| 537| mq | The handle of the message queue object | 538| buffer | Message content | 539| size | Message size | 540|**Return** | —— | 541| RT_EOK | Successful | 542| \-RT_EFULL | Message queue is full | 543| \-RT_ERROR | Fail | 544 545### Receive Message 546 547Only when there is a message in the message queue, can the receiver receive message, otherwise the receiver will set according to the timeout, or suspend on the waiting queue of the message queue, or return directly. The function interface to receive message is as follows: 548 549```c 550rt_err_t rt_mq_recv (rt_mq_t mq, void* buffer, 551 rt_size_t size, rt_int32_t timeout); 552``` 553 554When receiving a message, the receiver needs to specify the message queue object handle that stores the message and specify a memory buffer into which the contents of the received message will be copied. In addition, the timeout when the message is not received in time needs to be set. As shown in the figure below, the message on the front of the message queue is transferred to the end of the idle message linked list after receiving a message. The following table describes the input parameters and return values for this function: 555 556Input parameters and return values of rt_mq_recv() 557 558|**Parameters** |**Description** | 559|---------------|--------------------| 560| mq | The handle of the message queue object | 561| buffer | Message content | 562| size | Message size | 563| timeout | Specified timeout | 564|**Return** | —— | 565| RT_EOK | Received successfully | 566| \-RT_ETIMEOUT | Timeout | 567| \-RT_ERROR | Fail, return error | 568 569## Message Queue Application Example 570 571This is a message queue application routine. Two static threads are initialized in the routine, one thread will receive messages from the message queue; and another thread will periodically send regular messages and emergency messages to the message queue, as shown in the following code: 572 573Message queue usage routine 574 575```c 576#include <rtthread.h> 577 578/* Message queue control block */ 579static struct rt_messagequeue mq; 580/* The memory pool used to place messages in the message queue */ 581static rt_uint8_t msg_pool[2048]; 582 583ALIGN(RT_ALIGN_SIZE) 584static char thread1_stack[1024]; 585static struct rt_thread thread1; 586/* Thread 1 entry function */ 587static void thread1_entry(void *parameter) 588{ 589 char buf = 0; 590 rt_uint8_t cnt = 0; 591 592 while (1) 593 { 594 /* Receive messages from the message queue */ 595 if (rt_mq_recv(&mq, &buf, sizeof(buf), RT_WAITING_FOREVER) >= 0) 596 { 597 rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", buf); 598 if (cnt == 19) 599 { 600 break; 601 } 602 } 603 /* Delay 50ms */ 604 cnt++; 605 rt_thread_mdelay(50); 606 } 607 rt_kprintf("thread1: detach mq \n"); 608 rt_mq_detach(&mq); 609} 610 611ALIGN(RT_ALIGN_SIZE) 612static char thread2_stack[1024]; 613static struct rt_thread thread2; 614/* Thread 2 entry */ 615static void thread2_entry(void *parameter) 616{ 617 int result; 618 char buf = 'A'; 619 rt_uint8_t cnt = 0; 620 621 while (1) 622 { 623 if (cnt == 8) 624 { 625 /* Send emergency message to the message queue */ 626 result = rt_mq_urgent(&mq, &buf, 1); 627 if (result != RT_EOK) 628 { 629 rt_kprintf("rt_mq_urgent ERR\n"); 630 } 631 else 632 { 633 rt_kprintf("thread2: send urgent message - %c\n", buf); 634 } 635 } 636 else if (cnt>= 20)/* Exit after sending 20 messages */ 637 { 638 rt_kprintf("message queue stop send, thread2 quit\n"); 639 break; 640 } 641 else 642 { 643 /* Send a message to the message queue */ 644 result = rt_mq_send(&mq, &buf, 1); 645 if (result != RT_EOK) 646 { 647 rt_kprintf("rt_mq_send ERR\n"); 648 } 649 650 rt_kprintf("thread2: send message - %c\n", buf); 651 } 652 buf++; 653 cnt++; 654 /* Delay 5ms */ 655 rt_thread_mdelay(5); 656 } 657} 658 659/* Initialization of the message queue example */ 660int msgq_sample(void) 661{ 662 rt_err_t result; 663 664 /* Initialize the message queue */ 665 result = rt_mq_init(&mq, 666 "mqt", 667 &msg_pool[0], /* Memory pool points to msg_pool */ 668 1, /* The size of each message is 1 byte */ 669 sizeof(msg_pool), /* The size of the memory pool is the size of msg_pool */ 670 RT_IPC_FLAG_FIFO); /* If there are multiple threads waiting, assign messages in first come first get mode. */ 671 672 if (result != RT_EOK) 673 { 674 rt_kprintf("init message queue failed.\n"); 675 return -1; 676 } 677 678 rt_thread_init(&thread1, 679 "thread1", 680 thread1_entry, 681 RT_NULL, 682 &thread1_stack[0], 683 sizeof(thread1_stack), 25, 5); 684 rt_thread_startup(&thread1); 685 686 rt_thread_init(&thread2, 687 "thread2", 688 thread2_entry, 689 RT_NULL, 690 &thread2_stack[0], 691 sizeof(thread2_stack), 25, 5); 692 rt_thread_startup(&thread2); 693 694 return 0; 695} 696 697/* Export to the msh command list */ 698MSH_CMD_EXPORT(msgq_sample, msgq sample); 699``` 700 701The simulation results are as follows: 702 703``` 704\ | / 705- RT - Thread Operating System 706 / | \ 3.1.0 build Aug 24 2018 707 2006 - 2018 Copyright by rt-thread team 708msh > msgq_sample 709msh >thread2: send message - A 710thread1: recv msg from msg queue, the content:A 711thread2: send message - B 712thread2: send message - C 713thread2: send message - D 714thread2: send message - E 715thread1: recv msg from msg queue, the content:B 716thread2: send message - F 717thread2: send message - G 718thread2: send message - H 719thread2: send urgent message - I 720thread2: send message - J 721thread1: recv msg from msg queue, the content:I 722thread2: send message - K 723thread2: send message - L 724thread2: send message - M 725thread2: send message - N 726thread2: send message - O 727thread1: recv msg from msg queue, the content:C 728thread2: send message - P 729thread2: send message - Q 730thread2: send message - R 731thread2: send message - S 732thread2: send message - T 733thread1: recv msg from msg queue, the content:D 734message queue stop send, thread2 quit 735thread1: recv msg from msg queue, the content:E 736thread1: recv msg from msg queue, the content:F 737thread1: recv msg from msg queue, the content:G 738… 739thread1: recv msg from msg queue, the content:T 740thread1: detach mq 741``` 742 743The routine demonstrates how to use message queue. Thread 1 receives messages from the message queue; thread 2 periodically sends regular and emergency messages to the message queue. Since the message "I" that thread 2 sent is an emergency message, it will be inserted directly into the front of the message queue. So after receiving the message "B", thread 1 receives the emergency message and then receives the message "C". 744 745## Occasions to Use Message Queue 746 747Message Queue can be used where occasional long messages are sent, including exchanging message between threads and threads and sending message to threads in interrupt service routines (interrupt service routines cannot receive messages). The following sections describe the use of message queues from two perspectives, sending messages and synchronizing messages. 748 749### Sending Messages 750 751The obvious difference between message queue and mailbox is that the length of the message is not limited to 4 bytes. In addition, message queue also includes a function interface for sending emergency messages. But when you create a message queue with a maximum length of 4 bytes for all messages, the message queue object will be reduced to a mailbox. This unrestricted message is also reflected in the case of code writing which is also a mailbox-like code: 752 753```c 754struct msg 755{ 756 rt_uint8_t *data_ptr; /* Data block starting address */ 757 rt_uint32_t data_size; /* Data block size */ 758}; 759``` 760 761Similar to the mailbox example is the message structure definition. Now, let's assume that such a message needs to be sent to thread receiving messages. In the mailbox example, this structure can only send pointers pointing to this structure (after the function pointer is sent, the thread receiving messages can access the content pointing to this address, usually this piece of data needs to be left to the thread receiving the messages to release). How message queues is used is quite different: 762 763```c 764void send_op(void *data, rt_size_t length) 765{ 766 struct msg msg_ptr; 767 768 msg_ptr.data_ptr = data; /* Point to the corresponding data block address */ 769 msg_ptr.data_size = length; /* Datablock length */ 770 771 /* Send this message pointer to the mq message queue */ 772 rt_mq_send(mq, (void*)&msg_ptr, sizeof(struct msg)); 773} 774``` 775 776Note that in the above code, the data content of a local variable is sent to the message queue. In the thread that receives message, the same structure that receives messages using local variables is used: 777 778```c 779void message_handler() 780{ 781 struct msg msg_ptr; /* Local variable used to place the message */ 782 783 /* Receive messages from the message queue into msg_ptr */ 784 if (rt_mq_recv(mq, (void*)&msg_ptr, sizeof(struct msg)) >= 0) 785 { 786 /* Successfully received the message, corresponding data processing is performed */ 787 } 788} 789``` 790 791Because message queue is a direct copy of data content, so in the above example, local structure is used to save the message structure, which eliminates the trouble of dynamic memory allocation (and no need to worry, because message memory space has already been released when the thread receives the message). 792 793### Synchronizing Messages 794 795In general system designs, the problem of sending synchronous messages is often encountered. At this time, corresponding implementations can be selected according to the state of the time: two threads can be implemented in the form of [message queue + semaphore or mailbox]. The thread sending messages sends the corresponding message to the message queue in the form of message sending. After the message is sent, it is hoping to receive the confirmation from the threads receiving messages. The working diagram is as shown below: 796 797 798 799Depending on the message confirmation, the message structure can be defined as: 800 801```c 802struct msg 803{ 804 /* Other members of the message structure */ 805 struct rt_mailbox ack; 806}; 807/* or */ 808struct msg 809{ 810 /* Other members of the message structure */ 811 struct rt_semaphore ack; 812}; 813``` 814 815The first type of message uses a mailbox as a confirmation flag, while the second type of message uses a semaphore as a confirmation flag. The mailbox is used as a confirmation flag, which means that the receiving thread can notify some status values to the thread sending messages; and the semaphore is used as a confirmation flag that can only notify the thread sending messages in a single way, and the message has been confirmed to be received. 816 817# Signal 818 819A signal (also known as a soft interrupt signal), from a software perspective, is a simulation of interrupt mechanism. When it comes to its principle, thread receiving a signal is similar to processor receiving an interrupt request. 820 821## Signal Working Mechanism 822 823Signals are used for asynchronous communication in RT-Thread. The POSIX standard defines that sigset_t type defines a signal set. However, the sigset_t type may be defined differently in different systems. In RT-Thread, sigset_t is defined as unsigned long and named rt_sigset_t, the signals that the application can use are SIGUSR1(10) and SIGUSR2(12). 824 825The essence of signal is soft interrupt, which is used to notify the thread that an asynchronous event has occurred. It is used to notify abnormality and handle emergency between threads. No operation is needed for a thread to wait for the signal's arrival. In fact, the thread does not know when the signal will arrive. Threads can send soft interrupt signals by calling `rt_thread_kill()` between each other. 826 827The thread that receives the signals has different processing methods for various signals, and these methods can be divided into three categories: 828 829The first one is an interrupt-like processing program. For signals that need to be processed, the thread can specify a function to process. 830 831The second one is to ignore a signal and do nothing about it, as if it had not happened. 832 833The third one is to reserve the default value of the system for processing the signal. 834 835As shown in the following figure, suppose that thread 1 needs to process the signal. First, thread 1 installs a signal and unmasks it. At the same time, it sets the approach of how to process abnormality of signals. Then other threads can send a signal to thread 1, triggering thread 1 to process the signal. 836 837 838 839When the signal is passed to thread 1, if it is suspended, it will be switched to ready to process the corresponding signal. If it is running, it will create a new stack frame space on its current thread stack to process the corresponding signal. It should be noted that the thread stack size used will also increase accordingly. 840 841## Management of Signals 842 843Operations on signals include: install signal, block signal, unblock signal, send signal, and wait for signal. The interfaces of the signal are shown in the following figure: 844 845 846 847### Install Signal 848 849If the thread is to process a signal, then the signal needs to be installed in the thread. Signal installation is primarily used to determine the mapping relation between the signal value and the actions of the thread on that signal value, that is what signal is to be processed, what actions will be taken when the signal is passed to the thread. See the following code for a detailed definition: 850 851```c 852rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t[] handler); 853``` 854 855`rt_sighandler_t` is the type of function pointer that defines the signal process function. The following table describes the input parameters and return values for this function: 856 857Input parameters and return values for rt_signal_install() 858 859|**Parameters** |**Description** | 860|-----------------------|--------------------------------------------------------| 861| signo | Signal value (only SIGUSR1 and SIGUSR2 are open to the user, the same applies below) | 862| handler | Set the approach to process signal values | 863|**Return** | —— | 864| SIG_ERR | Wrong signal | 865| The handler value before the signal is installed | Successful | 866 867Setting the handler parameter during signal installation determines the different processing methods for this signal. Processing methods can be divided into three types: 868 8691) Similar to the interrupt processing method, the parameter points to the user-defined processing function when the signal occurs, and is processed by the function. 870 8712) The parameter is set to SIG_IGN. Ignore a signal, and do nothing on the signal, as if it had not happened. 872 8733) The parameter is set to SIG_DFL, and the system will call the default function _signal_default_handler() to process. 874 875### Block Signal 876 877Blocking signal can also be understood as shielding signals. If the signal is blocked, the signal will not be delivered to the thread that installed the signal and will not cause soft interrupt processing. Call rt_signal_mask() to block the signal: 878 879```c 880void rt_signal_mask(int signo); 881``` 882 883The following table describes the input parameters for this function: 884 885rt_signal_mask() function parameters 886 887|**Parameters**|**Description**| 888|----------|----------| 889| signo | Signal value | 890 891### Unblock Signal 892 893Several signals can be installed in the thread. Using this function can give some "attention" to some of the signals, then sending these signals will cause a soft interrupt for the thread. Calling rt_signal_unmask() to unblock the signal: 894 895```c 896void rt_signal_unmask(int signo); 897``` 898 899The following table describes the input parameters for this function: 900 901rt_signal_unmask() function parameters 902 903|**Parameters**|**Description**| 904|----------|----------| 905| signo | Signal value | 906 907### Send Signals 908 909When we need to process abnormality, a signal can be sent to the thread that has been set to process abnormality. Call rt_thread_kill() to send a signal to any thread: 910 911```c 912int rt_thread_kill(rt_thread_t tid, int sig); 913``` 914 915The following table describes the input parameters and return values for this function: 916 917Input parameters and return values for rt_thread_kill() 918 919|**Parameters** |**Description** | 920|-------------|----------------| 921| tid | The thread receiving signal | 922| sig | Signal value | 923|**Return** | —— | 924| RT_EOK | Sent successfully | 925| \-RT_EINVAL | Parameter error | 926 927### Wait for Signal 928 929Wait for the arrival of the set signal. If this signal did not arrive, suspend the thread until the signal arrival or the wait until time exceeds the specified timeout. If the signal arrived, the pointer pointing to the signal body is stored in si, as follows is the function to wait for signal. 930 931```c 932int rt_signal_wait(const rt_sigset_t *set, 933 rt_siginfo_t[] *si, rt_int32_t timeout); 934``` 935 936rt_siginfo_t is the data type that defines the signal information, and the following table describes the input parameters and return values of the function: 937 938Input parameters and return values for rt_signal_wait() 939 940|**Parameters** |**Description** | 941|---------------|----------------------------| 942| set | Specify the signal to wait | 943| si | Pointer pointing to signal information | 944| timeout | Waiting time | 945|**Return** | —— | 946| RT_EOK | Signal arrives | 947| \-RT_ETIMEOUT | Timeout | 948| \-RT_EINVAL | Parameter error | 949 950## Signal Application Example 951 952This is a signal application routine, as shown in the following code. This routine creates one thread. When the signal is installed, the signal processing mode is set to custom processing. The processing function of the signal is defined to be thread1_signal_handler(). After the thread is running and the signal is installed, a signal is sent to this thread. This thread will receive the signal and print the message. 953 954Signal usage routine 955 956```c 957#include <rtthread.h> 958 959#define THREAD_PRIORITY 25 960#define THREAD_STACK_SIZE 512 961#define THREAD_TIMESLICE 5 962 963static rt_thread_t tid1 = RT_NULL; 964 965/* Signal process function for thread 1 signal handler */ 966void thread1_signal_handler(int sig) 967{ 968 rt_kprintf("thread1 received signal %d\n", sig); 969} 970 971/* Entry function for thread 1 */ 972static void thread1_entry(void *parameter) 973{ 974 int cnt = 0; 975 976 /* Install signal */ 977 rt_signal_install(SIGUSR1, thread1_signal_handler); 978 rt_signal_unmask(SIGUSR1); 979 980 /* Run for 10 times */ 981 while (cnt < 10) 982 { 983 /* Thread 1 runs with low-priority and prints the count value all through*/ 984 rt_kprintf("thread1 count : %d\n", cnt); 985 986 cnt++; 987 rt_thread_mdelay(100); 988 } 989} 990 991/* Initialization of the signal example */ 992int signal_sample(void) 993{ 994 /* Create thread 1 */ 995 tid1 = rt_thread_create("thread1", 996 thread1_entry, RT_NULL, 997 THREAD_STACK_SIZE, 998 THREAD_PRIORITY, THREAD_TIMESLICE); 999 1000 if (tid1 != RT_NULL) 1001 rt_thread_startup(tid1); 1002 1003 rt_thread_mdelay(300); 1004 1005 /* Send signal SIGUSR1 to thread 1 */ 1006 rt_thread_kill(tid1, SIGUSR1); 1007 1008 return 0; 1009} 1010 1011/* Export to the msh command list */ 1012MSH_CMD_EXPORT(signal_sample, signal sample); 1013``` 1014 1015The simulation results are as follows: 1016 1017``` 1018 \ | / 1019- RT - Thread Operating System 1020 / | \ 3.1.0 build Aug 24 2018 1021 2006 - 2018 Copyright by rt-thread team 1022msh >signal_sample 1023thread1 count : 0 1024thread1 count : 1 1025thread1 count : 2 1026msh >thread1 received signal 10 1027thread1 count : 3 1028thread1 count : 4 1029thread1 count : 5 1030thread1 count : 6 1031thread1 count : 7 1032thread1 count : 8 1033thread1 count : 9 1034``` 1035 1036In the routine, the thread first installs the signal and unblocks it, then sends a signal to the thread. The thread receives the signal and prints out the signal received: SIGUSR1 (10). 1037 1038 1039