Processes¶
Overview¶
MiraOS applications are typically written by using processes. Processes in MiraOS are built on top of a light-weight multi-threading
mechanism called protothreads. Protothreads are very light-weight with low process-switching
overhead, however each process does not have its own stack, so all variables that need to preserve their content over process switches
need to be declared as static
.
Processes are scheduled cooperatively, which means that each process is responsible for voluntarily passing control back to the operating system without executing for too long. Hence, the application developer must make sure that operations that are long (more than a few ms) are split into multiple process schedulings, allowing such operations to resume after the operating system has executed other processes.
Defining a process¶
Before a process can be defined, it must first be declared at the top of the source file.
A process is declared using the PROCESS()
macro that takes two arguments: one is the process' symbol, used to refer to the process later on, and the other is a string with the name of the process.
#include "mira.h" /* Main include file for MiraOS. */
#include <stdio.h> /* We want printf later. */
PROCESS(my_proc, "My process");
We can now define our process:
PROCESS_THREAD(my_proc, ev, data)
{
PROCESS_BEGIN();
printf("This is my process\n");
PROCESS_END();
}
The PROCESS_THREAD()
macro takes the identifier of the processes specified by the PROCESS()
declaration we made earlier.
The ev
and data
arguments are for the event handling (the data
argument can also be used when starting a process for
passing data into the process by the process_start()
system call), more about event handling in the Events section.
For now, we just ignore the event handling, since it is not needed for this simple example.
All processes must have the PROCESS_BEGIN()
statement, it marks the start of the process execution. While it's possible to
place code before this statement, developers should typically not do so except for variable definitions.
The process shall end with the PROCESS_END()
statement. All code will go between these statements. In this simple
example, the process just calls to printf
then implicitly stops the process.
Starting a process¶
A process can be started using the process_start()
system call. This system call takes two arguments: first the process handler,
then a data pointer that can be used to pass data to the newly started process.
process_start(&my_proc, NULL);
This starts the previously declared process.
Stopping a process¶
A process can be stopped in any of three different ways:
- The process may implicitly exit by reaching the
PROCESS_END()
statement. - The process exit explicitly by calling
PROCESS_EXIT()
in thePROCESS_THREAD
body. - Another process kills the process by calling the
process_exit()
system call.
After a process has been stopped, it may be started again by calling the process_start()
system call.
Yielding and pausing¶
A process may voluntarily release control to the scheduler. This can be done by calling PROCESS_PAUSE()
.
When PROCESS_PAUSE()
is called, the scheduler immediately reschedules the paused process for execution and
delivers any unprocessed events to other processes. Execution of the paused process is resumed after the
execution of any other scheduled processes has been performed.
Alternatively, a process can yield using the PROCESS_YIELD()
call. This will also return control to the
scheduler, except this time the process will not be executed again until an event is sent to the process.
PROCESS_THREAD(some_proc, ev, data)
{
PROCESS_BEGIN();
while (1) {
do_some_operations();
PROCESS_PAUSE();
/* execution will resume as soon as scheduler
has executed other processes */
do_some_more_operations();
PROCESS_YIELD();
/* execution will resume when an event is
delivered to the process */
}
PROCESS_END();
}