Go to the first, previous, next, last section, table of contents.

Multi-Tasking

Overview

One of the most powerful features of IC is its multi-tasking facility. Processes can be created and destroyed dynamically during run-time.

Any C function can be spawned as a separate task. Multiple tasks running the same code, but with their own local variables, can be created.

Processes communicate through global variables: one process can set a global to some value, and another process can read the value of that global.

Each time a process runs, it executes for a certain number of ticks, defined in milliseconds. This value is determined for each process at the time it is created. The default number of ticks is five; therefore, a default process will run for 5 milliseconds until its "turn" ends and the next process is run. All processes are kept track of in a process table; each time through the table, each process runs once (for an amount of time equal to its number of ticks).

Each process has its own program stack. The stack is used to pass arguments for function calls, store local variables, and store return addresses from function calls. The size of this stack is defined at the time a process is created. The default size of a process stack is 256 bytes.

Processes that make extensive use of recursion or use large local arrays will probably require a stack size larger than the default. Each function call requires two stack bytes (for the return address) plus the number of argument bytes; if the function that is called creates local variables, then they also use up stack space. In addition, C expressions create intermediate values that are stored on the stack.

It is up to the programmer to determine if a particular process requires a stack size larger than the default. A process may also be created with a stack size smaller than the default, in order to save stack memory space, if it is known that the process will not require the full default amount.

When a process is created, it is assigned a unique process identification number or pid. This number can be used to kill a process.

Creating New Processes

The function to create a new process is start_process. start_process takes one mandatory argument--the function call to be started as a process. There are two optional arguments: the process's number of ticks and stack size. (If only one optional argument is given, it is assumed to be the ticks number, and the default stack size is used.)

start_process has the following syntax:

int start_process(function-call(...),[TICKS],[STACK-SIZE])

start_process returns an integer, which is the process ID assigned to the new process.

The function call may be any valid call of the function used. The following code shows the function main creating a process:

void check_sensor(int n)
{
  while (1)
    printf("Sensor %d is %d\n", n, digital(n));
}

void main()
{
  start_process(check_sensor(2));
}

Normally when a C functions ends, it exits with a return value or the "void" value. If a function invoked as a process ends, it "dies," letting its return value (if there was one) disappear. (This is okay, because processes communicate results by storing them in globals, not by returning them as return values.) Hence in the above example, the check_sensor function is defined as an infinite loop, so as to run forever (until the board is reset or a kill_process is executed).

Creating a process with a non-default number of ticks or a non-default stack size is simply a matter of using start_process with optional arguments; e.g.

start_process(check_sensor(2), 1, 50);

will create a check_sensor process that runs for 1 milliseconds per invocation and has a stack size of 50 bytes (for the given definition of check_sensor, a small stack space would be sufficient).

Destroying Processes

The kill_process function is used to destroy processes. Processes are destroyed by passing their process ID number to kill_process, according to the following syntax:

int kill_process(int pid);

kill_process returns a value indicating if the operation was successful. If the return value is 0, then the process was destroyed. If the return value is 1, then the process was not found.

The following code shows the main process creating a check_sensor process, and then destroying it one second later:

void main()
{
  int pid;

  pid= start_process(check_sensor(2));
  sleep(1.0);
  kill_process(pid);
}

Process Management Commands

IC has two commands to help with process management. The commands only work when used at the IC command line. They are not C functions that can be used in code.

kill_all
kills all currently running processes.
ps
prints out a list of the process status. The following information is presented: process ID, status code, program counter, stack pointer, stack pointer origin, number of ticks, and name of function that is currently executing.

Process Management Library Functions

The following functions are implemented in the standard C library.

void hog_processor()
Allocates an additional 256 milliseconds of execution to the currently running process. If this function is called repeatedly, the system will wedge and only execute the process that is calling hog_processor(). Only a system reset will unwedge from this state. Needless to say, this function should be used with extreme care, and should not be placed in a loop, unless wedging the machine is the desired outcome.
void defer()
Makes a process swap out immediately after the function is called. Useful if a process knows that it will not need to do any work until the next time around the scheduler loop. defer() is implemented as a C built-in function.

Go to the first, previous, next, last section, table of contents.