![]() |
AKOS
v1.0.0
Documentation
|
The work is easier to manage when it is split into clean tasks or threads. Each thread takes responsibility for one part of the problem.
In a bare-metal project, the main function often looks like this:
In AKOS, a thread entry function can look like this:
Do not think that thread_A is a thread object. It is only the function that defines what the thread does. To create a thread statically in AKOS, use the macro below:
The thread_t object created by this macro is a static descriptor, not the runtime thread object itself. During initialization, AKOS uses this descriptor to allocate and configure the actual Task Control Block (TCB) for the thread.
The TCB is the runtime data structure that AKOS uses to manage a thread. It is opaque to application code, but the scheduler depends on it for fast access to the thread's state.
At a high level, the TCB stores:
In the code, the TCB also keeps stk_ptr as the first field. That layout is important because the port code can access the saved stack pointer directly during a context switch.
Why does a thread need these parameters?
Those parameter descriptions make more sense when you understand the TCB. When calling AKOS_THREAD_DEFINE, AKOS invokes akos_memory_malloc to allocate memory for the stack. Then it invokes akos_memory_malloc one more time to allocate memory for the TCB.
When many threads are created, the memory will look like this:
In AKOS, a thread can be in one of these states:
The exact thread lifecycle is shown below:
Every thread requires its own stack buffer so the CPU can save and restore the thread context. In AKOS, stack size is specified in 32-bit words, because the port builds the initial stack frame using 32-bit registers.
When you define a thread stack, make sure it is large enough for:
AKOS allocates the stack buffer first, then initializes the initial stack frame from the top of that buffer.
One way to estimate the size of stack (ref from micriumOS-III) is manually calculate the sum of:
The most elegant way is using Memory Management Unit (MMU) or a Memory Protection Unit (MPU) if the CPU support that features. Basically, reserve a guard region around the stack and mark it as inaccessible. If the thread stack grows into that protected area, the CPU raises a fault immediately, so the kernel can detect the overflow before it corrupts other memory. This is also called Redzone MPU/MMU.
This approach also reserves a guard region called Redzone and fills that region with a known pattern. Then, every time context switching occurs and before the thread is switched in, the kernel checks whether the Redzone still contains the expected values. If the pattern has been changed, it means the stack has overflowed into the guard region, so the kernel can detect the problem early.
Thread priorities are integer numbers from 0 to OS_CFG_PRIO_MAX - 1. The lowest number means the highest priority, and vice-versa. For example, thread A with priority number 3 has a higher priority than thread B with priority number 7. Threads with the same priority are considered cooperative threads, and threads with different priorities are considered preemptible threads.
AKOS currently defines static threads, so a thread's priority cannot be changed after it is defined.
AKOS also defines some kernel threads during initialization: