ARM Exception Levels (ELs) define a hierarchy of privilege levels for code execution, controlling access to system resources:
- EL0 (User Mode)
- EL1 (Kernel Mode)
- EL2 (Hypervisor Mode)
- EL3 (Secure Monitor Mode)
TrustZone technology creates isolated execution environments within the processor, dividing the system into two worlds:
- Non-Secure World (Normal World)
- Secure World
The integration of Exception Levels and TrustZones allows for different topologies with practical benefits. Generally, the Secure Monitor (EL3) manages transitions between Secure and Non-Secure Worlds, protecting sensitive operations. The Hypervisor (EL2) manages virtual machines in both worlds, ensuring virtualization while maintaining security. The Secure World runs secure code at EL0 and EL1, isolated from the Non-Secure World. In the Non-Secure World, EL0 is typically for applications, and EL1 is for operating systems. The boot order starts from high to low exception levels in the Secure World, followed by non-secure exception levels from EL3 to EL0. This approach fosters modularity, enhancing fault isolation and potentially improving security and stability. However, it can also introduce performance trade-offs due to the overhead of exception level changes. Therefore, the optimal topology should consolidate as much as possible within one exception level.
Development involves creating and maintaining separate components that communicate through well-defined interfaces, making development more modular and testing easier. Each component has its own address space and communicates through well-defined remote procedure calls (RPC), commonly referred to as a service-oriented architecture.
IPC mechanisms enable communication and data sharing across process boundaries using kernel-managed structures like pipes, message queues, and shared memory. Each method has its own use cases and advantages, ranging from simple signaling to complex data exchange., but in all cases the kernel management could be drilled down to a minimum. For example VirtIO allows to give full communication abstraction to the involved peers.
Address space encloses several segments:
- Code Segment (Text Segment): Contains the executable instructions of the program, typically read-only to prevent accidental modification.
- Data Segment: Stores global and static variables initialized before execution, divided into initialized data and uninitialized data (BSS) segments.
- Heap: A region for dynamic memory allocation, managed at runtime using functions like malloc and free in C/C++.
- Stack: Used for function call management, local variables, and control flow, growing and shrinking as functions are called and return.
- Memory-Mapped Files and Shared Libraries: Includes dynamically loaded libraries and memory-mapped I/O regions that allow files or devices to be accessed as part of the program’s memory.
- Reserved Space: Areas reserved by the operating system for special purposes, such as kernel space or hardware-specific regions.
- Thread-Local Storage (TLS): A section for storing data unique to each thread in a multi-threaded program.
Here are some common IPC methods and how they work across address spaces:
- Pipes and Named Pipes (FIFOs)
- Unnamed Pipes: Used for communication between parent and child processes. Data written to the pipe by one process can be read by another.
- Example: pipe(), read(), write() system calls in Unix/Linux.
- Named Pipes (FIFOs): Similar to unnamed pipes but have a name in the filesystem, allowing unrelated processes to communicate.
- Example: mkfifo(), open(), read(), write() system calls.
- Unnamed Pipes: Used for communication between parent and child processes. Data written to the pipe by one process can be read by another.
- Message Queues
- Allow processes to send and receive messages in a queued manner. Messages are stored in a queue and can be retrieved in a FIFO (First In, First Out) order.
- Example: msgget(), msgsnd(), msgrcv() system calls.
- Allow processes to send and receive messages in a queued manner. Messages are stored in a queue and can be retrieved in a FIFO (First In, First Out) order.
- Shared Memory
- Allows multiple processes to access the same memory region, providing the fastest form of IPC because data does not need to be copied between processes.
- Example: shmget(), shmat(), shmdt() system calls.
- Allows multiple processes to access the same memory region, providing the fastest form of IPC because data does not need to be copied between processes.
- Semaphores
- Used to synchronize access to shared resources, helping prevent race conditions by controlling access to shared memory segments.
- Example: semget(), semop(), semctl() system calls.
- Used to synchronize access to shared resources, helping prevent race conditions by controlling access to shared memory segments.
- Sockets
- Provide communication between processes on the same or different machines. They support various protocols (e.g., TCP, UDP) and can handle stream and datagram communication.
- Example: socket(), bind(), listen(), accept(), connect(), send(), recv() system calls.
- Provide communication between processes on the same or different machines. They support various protocols (e.g., TCP, UDP) and can handle stream and datagram communication.
- Signals
- A limited form of IPC used to notify a process that a particular event has occurred. They can be used for process control and synchronization.
- Example: kill(), signal(), sigaction() system calls.
- A limited form of IPC used to notify a process that a particular event has occurred. They can be used for process control and synchronization.
- D-Bus
- A high-level IPC mechanism used in Linux and other Unix-like systems. It allows communication between processes running in the same session or system bus.
- Example: dbus-send, dbus-monitor commands.
- A high-level IPC mechanism used in Linux and other Unix-like systems. It allows communication between processes running in the same session or system bus.
Here is where POSIX (et al) comes into the play.