Skip to content

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 first must 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:

  1. The process may implicitly exit by reaching the PROCESS_END() statement.
  2. The process exit explicitly by calling PROCESS_EXIT() in the PROCESS_THREAD body.
  3. 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();
}