Understanding Inter-Task Communication in RTOS

Follow+Star PublicAccount, don’t miss out on exciting content

Understanding Inter-Task Communication in RTOS

Author | strongerHuang

WeChat Official Account | strongerHuang

I came across an interesting question: why do RTOS tasks not use global variables for inter-task communication when bare-metal code uses global variables?
Those who have a deep understanding of RTOS principles, or have read RTOS source code should know: RTOS typically implements inter-task communication through a series of pointer operations.
The “effective data” for inter-task communication is actually also implemented by pointers pointing to a “variable” or “array”.
1.Semaphore

A semaphore essentially conveys an “event”. For example: Task A completes sending data, and notifies Task B through the semaphore.

OSSemPost(EventSem_SendOK);

We mainly want to conveythe completion of sending data as this “event.

2.Queue

The principle of queues is somewhat similar to that of semaphores, but here it is a “variable”. For example: after a serial port receives a complete frame of data, it sends it to Task B through the queue.

OSQPost(UARTRcvQueue, RcvBuf);

Compared to semaphores, queues can convey a larger amount of data, as the effective data conveyed by queues is generally an “array“.

There are also mailboxes, which are similar to queues and can be understood as “two-dimensional arrays”.

By this point, you will find that whether it is a semaphore or a queue, the underlying essence is also passing “variables” and “arrays”.

Now the question arises: Why doesn’t RTOS use global variables for inter-task communication?

This question is quite common, and I have seen discussions about it in my technical group, so I will simply share my views.

What are the issues with global variables?

The reason RTOS does not use global variables for inter-task communication is that there are many drawbacks to using global variables.
1. Preemption Issues
If two or more tasks need to “use” the same global variable without any “mutual exclusion” measures, there will inevitably be preemption issues.
2. Code Standardization Issues
If there are only a few global variables in the entire project, it is not a problem. However, if there are dozens or even hundreds of global variables, do you think such code will be easy to maintain later?
After multiple iterations, the code will only become harder to understand and read.
3. Coupling Issues
Global variables lead to unreasonable layering and conflict with modular programming. Your global variables have no ownership; they are neither Task A nor Module A, which may ultimately lead to “being butchered” and result in “abandonment”.
4. Security Issues
There is a saying that global variables are the “culprit” of projects, and after a project grows larger, once there is a small modification, it may trigger a big bug.
Understanding Inter-Task Communication in RTOS
Global variables have many drawbacks, but I won’t describe them all here. In short: be cautious with global variables.
Of course, the issues described above (drawbacks) are based on the existence of multiple variables in the project. If there are only 1 or 2 global variables, this is not within the scope of this article.

Principles of Semaphore and Queue Communication

Most RTOS semaphores and queues use pointers, structures, arrays, etc., combined with the system for “encapsulation”, making inter-task communication more effective and secure, while also adhering to the principle of “high cohesion and low coupling”.
For example, the semaphore post in ucos:
INT8U  OSSemPost (OS_EVENT *pevent){#if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */    OS_CPU_SR  cpu_sr = 0u;#endif
#if OS_ARG_CHK_EN > 0u    if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */        return (OS_ERR_PEVENT_NULL);    }#endif    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   /* Validate event block type                     */        return (OS_ERR_EVENT_TYPE);    }    OS_ENTER_CRITICAL();    if (pevent->OSEventGrp != 0u) {                   /* See if any task waiting for semaphore         */                                                      /* Ready HPT waiting on event                    */        (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);        OS_EXIT_CRITICAL();        OS_Sched();                                   /* Find HPT ready to run                         */        return (OS_ERR_NONE);    }    if (pevent->OSEventCnt < 65535u) {                /* Make sure semaphore will not overflow         */        pevent->OSEventCnt++;                         /* Increment semaphore count to register event   */        OS_EXIT_CRITICAL();        return (OS_ERR_NONE);    }    OS_EXIT_CRITICAL();                               /* Semaphore value has reached its maximum       */    return (OS_ERR_SEM_OVF);}

Although we only need to convey effective information with one variable, it manages the “critical section” and predicts some erroneous situations, etc.

In the end, RTOS source code can also be considered an excellent project, especially those RTOS that are currently widely used and have a large installed base, such asµC/OS, FreeRTOS, RT-Thread, ThreadX etc.

Lastly, if you have time, I encourage you to read the RTOS source code, as the RTOS kernel I recommend is µC/OS. Reading the source code will help you grasp some software architecture knowledge and understand common issues in the development process.

———— END ————

Understanding Inter-Task Communication in RTOS

●Column “Embedded Tools”

●Column “Embedded Development”

●Column “Keil Tutorials”

●Selected Tutorials from Embedded Column

Follow the public account and reply “Join Group” to join the technical group according to the rules, reply “1024” to see more content.

Understanding Inter-Task Communication in RTOS

Understanding Inter-Task Communication in RTOS

Click “Read Original” for more shares.

Leave a Comment

×