FreeRTOS has a category page for inter-task communication on FreeRTOS.org
IPC and synchronization are closely-related topics because simultaneous access of a shared resource is a race condition, where the state of that resource becomes indeterminate.
With preemption enabled, it is possible for instructions in two tasks to be interleaved in such a way that either task can see the shared resource in the middle of the other’s access. Interrupting a more complex procedure like adding a node to a linked list can put the list in an incomplete state if interrupted.
One thing to watch out for is that “Interprocess Communication” often assumes that the two processes can’t just “share memory” with each other, but need some communication medium between them to share information. In a “Real-Time” context, large data structures WILL be typically just be shared, and some synchronization method is used to manage it.
Synchronization tends to be the method used by the processes to let each other determine who “controls” the data, and can change it, and when it is safe for the other to look at it. As cookpate points out, two tasks trying to update the same chunk of data can lead to corruption, as can trying to read the data in the middle of an update might lead to inconsistent data.
I’ve been trying to wrap my head around the concept of Interprocess Communication (IPC), and while I understand the definition that “IPC is essential when processes need to exchange information but they can’t directly access each other’s memory,” I’m struggling to think of concrete real-world situations where this comes into play. Can anyone provide some practical examples?
In IoT, most WiFi/Ethernet/Bluetooth network interfaces are generally in high-priority tasks. An interface is responsible for the data transfer across the physical and link layers. Its task is high-priority because the protocols have precise timing requirements. The interface also handles errors and notifies another task whenever the network status changes.
A lower-priority task can use two stream buffers to transfer inbound and outbound data between the network interface task. A TCP/IP stack can run in this task sequence any data received from the network interface into TCP/IP messages and send any outbound TCP/IP messages onto the network interface. The TCP/IP stack does not require tight timing, but may require sending/receiving messages larger than what the link layer can support.
Stream buffers pass continuous stream of bytes from one task or an interrupt to another task.
The network interface task reads from the outbound stream until its empty, and writes to the inbound stream until its full before blocking. The network interface task may also block whenever the network device is idle. Blocking in any case allows the TCP/IP task to resume processing messages.
Decoupling these two parts of the network stack into two tasks reduces the complexity of managing both a network device and a transport protocol. The network device or transport protocol on top of it may also be swapped as long as they use the same stream buffer interface.
As @richard-damon already explained, the thing about not being able to access memory does not completely apply to MCUs (assuming that you are not using a port with Memory Protection Support). Lets say a task T1, wants to send some data to another task T2, it can use a queue for that and achieve inter-task communication.
I think of “IPC” as a technique used between separate computers over a network or between isolated processes on a “big iron” machine with memory protection separating them. With FreeRTOS, this only applies for ports that support a MPU and to restricted tasks. When you have natural sharing of memory between the tasks many of the concepts just don’t apply. This is one of the reason FreeRTOS has “Tasks” and not “Processes”, so you don’t have “Processes” to be “Inter”, the isolation just isn’t of that level.
FreeRTOS Tasks are much more like “Threads” on bigger machines, but we often think of them a bit more separated (if not actually isolated) then we think of threads.
I would say that this is fairly accurate; however, in an RTOS context, you need to consider ISRs which are also “threads of execution” in the sense that some of the coordination mechanisms can also be employed by them. Needless to say, ISRs and tasks by definition are fundamentally distinct types of threads of execution, and the fact that “IPC mechanisms” can also be used by ISRs adds to confusion, but it is part of an RTOS to coordinate all strands/threads of execution.