Contiki Tutorial--Process

Source: Internet
Author: User
Tags event timer


The code in Contiki can run in one of the following two execution contexts: cooperative or preemptive. Cooperative code runs sequentially, and preemptive code can pause running cooperative code. The processes in Contiki run in a cooperative context, while interrupts and real-time timers run in a preemptive context.

All Contiki programs are called processes. A process is a code fragment that is routinely executed in a Contiki system. When the system is started, or when a module containing a process is loaded into the system, the process starts running. When something happens, the process runs, such as when a timer expires, or an external event is generated.

The code in Contiki can run in one of the following two execution contexts: cooperative or preemptive. The cooperative code runs sequentially. Once the cooperative code runs, it runs to completion, and then the other cooperative code is scheduled to run. Preemptive code can suspend cooperative code at any time. When the preemptive code suspends the cooperative code, the cooperative code must wait until the preemptive code runs to finish before resuming running again. The concept of two scheduling contexts in Contiki is as shown.

The normal process is always running in a cooperative context. Interrupt handlers in real-time tasks and device drivers can be run in a preemptive context. We'll go back to the subject of the real-time task when we talk about timer timers.

structure of the process

The process of Contiki consists of two parts: the Process Control block and the process thread. The process control block is stored in memory and contains information about the process runtime, such as the process name, process state, and pointers to process threads. A process thread is a block of code that is stored in a ROM.

Process Control block PCB

The Process Control block contains information for each process, such as the process state, a pointer to the thread that is pointing to the process, and the process's text name. Process Control blocks are used only within the kernel and cannot be accessed directly by the process.

The internal structure of the process Control block. User code cannot directly access any members of the process Control block.

struct Process {   struct process *next;   const char *name;   Int (* thread) (struct pt *,                  process_event_t,  process_data_t);   struct PT pt;   unsigned char state, needspoll; };

The process control block is lightweight and requires only a few bytes of memory. The structure of the process control block is shown above. No member of the struct can be accessed directly. Only process management functions can access these members. We describe the structure here only to illustrate how lightweight the process control block is. The first member of the Process Control block,next, points to the next process control block in the process chain list. The member name points to the name of the process's text type. The member thread is a function pointer that points to the thread of the process. The Member State and Needspoll are internal flags that are modified by the function Process_poll () when the process is rotation.

Process Control blocks are not directly defined and declared, but are passed through the macro process (). The macro has two parameters: the Process Control block variable name used to access the process, and the process text name used to debug and print the process. The definition of Process control in the Hello world routine looks like this:

Examples of Process Control blocks:

Process (hello_world_process, "Hello World Process");

Process Threads

The process thread contains the code for the process. The process thread is a single protothread that is dispatched by the process scheduler. Examples of process threads are as follows.

Examples of process threads:

Process_thread (hello_world_process, Ev, data) {   process_begin ();    printf ("Hello, world\n");    Process_end (); }


Protothread allows the system to run other activities while waiting for an event to occur. The concept of Protothread is presented in the process of developing Contiki, but the concept is not tied to Contiki. The Protothread also works well in many other systems.

Contiki running on memory-constrained systems, reducing memory load is especially important. Protothread provides a good way for C functions to run in a similar thread without the traditional thread memory load.

Protothread can be seen as a regular C function. The function uses two special macros as a start and end:pt_begin () and pt_end (). Between the two macros, you can use a series of protothread functions.

The C preprocessor implements the main operations of the Protothread:

struct PT {lc_t LC}; #define PT_WAITING 0 #define pt_exited  1 #define pt_ended   2 #define PT_INIT (PT)          lc_init (PT->LC) #define Pt_begin (PT)         Lc_resume (PT->LC) #define Pt_end (PT)           lc_end (PT->LC);                                  Return pt_ended #define PT_WAIT_UNTIL (PT, c) lc_set (PT->LC);                                  if (! ( c)                                           return pt_waiting #define PT_EXIT (PT)          return pt_exited

The local continuations is implemented through the C-language switch statement:

typedef unsigned short lc_t; #define LC_INIT (c)   c = 0 #define LC_RESUME (c) switch (c) {case 0: #define LC_SET (c)    C = __line__; case __line__: # Define Lc_end (c)    }

protothreads in the process

The Contiki process itself implements a set of protothread that allow the process to wait for an upcoming event. Therefore, the Protothread statements used in the Contiki process are slightly different from the pure Protothread statements described in the previous section.

Contiki process-related Protothread macros used in the process:

Process_begin (); Declares the beginning of a process ' protothread. Process_end (); Declares the end of a process ' protothread. Process_exit (); Exit the process. Process_wait_event (); Wait for any event. Process_wait_event_until (); Wait for the event, but with a condition. Process_yield (); Wait for any event, equivalent to Process_wait_event (). Process_wait_until (); Wait for a given condition; may not yield the process. Process_pause (); Temporarily yield the process.


In Contiki, the process runs when it receives an event. There are two kinds of events in Contiki: Asynchronous events and synchronous events.

When an asynchronous event is emitted, the event is placed in the event queue in the kernel and is passed over a period of time to the receiving process.

When a synchronization event is emitted, the event is immediately delivered to the receiving process.

Asynchronous Events

An asynchronous event cannot be passed to the receiving process until it has been issued for some time. It is stored in the event queue of the Contiki kernel after the event has been issued and during the time it was passed.

The kernel is responsible for passing events in the event queue to the receiving process. The kernel loops through the event queue and passes the events in the queue to the process through the calling process.

The recipient of an asynchronous event can be a special process, or it can be any generic process that is running. When the recipient is a special process, the kernel invokes the process and passes events into the process. When the event receiver is all the common processes in the system, the kernel passes the events one after the other to all processes.

Asynchronous events are emitted through the function process_post () . The internal implementation of Process_post () is simple, it checks the size of the current event queue, checks if there is room to store the event, and then makes the decision. If there is not enough space, the function returns an error. If there is enough space, the function inserts the event at the end of the event queue and then returns.

Synchronization Events

Unlike asynchronous events, synchronization events are emitted without passing through the event queue and are passed directly. Synchronization events can only be emitted to a specific process.

Because synchronization events are passed directly, passing a synchronization event is functionally equivalent to a function call: The receiving process is called directly and the sending process is blocked until the receiving process finishes processing the event. However, the receiving process is not told whether the event being emitted is a synchronous event or an asynchronous event.


A polling request is a special event. The process can be polled by calling the function Process_poll () request. After the process requests polling, it is called as soon as possible. When the process is polled, a special event is passed into the process.

Polling is the "the" process run from the interrupt. Theprocess_poll () function is the IF function in the process modulethat are safe to call from preemptive mode.

Event Identifiers

The event is identified by the event identifier. The event identifier is a 8-bit array that is passed to the receiving process. The receiving process can do different processing based on the different event identifiers that are received.

The range of event identifiers is 0~255. Event identifiers under 127 can be used freely in a user process, and more than 128 of event identifiers can only be used between different processes. Event identifiers above 128 are managed by the kernel.

Numbers starting at 128 are statically allocated by the kernel for different purposes. These identifiers are defined as follows.

Event identifiers reserved by the Contiki kernel:

#define Process_event_none            #define PROCESS_EVENT_INIT            129 #define PROCESS_EVENT_POLL            #define Process_event_exit            131 #define Process_event_continue        133 #define PROCESS_EVENT_MSG             134 #define Process_ event_exited          135 #define Process_event_timer           136

These event identifiers function as follows:

Process_event_none: The event identifier is not being used. Process_event_init: The event is sent to a new process that is initializing. Process_event_poll: The event is sent to a polling process. Process_event_exit: This event is sent to a process that is being killed by the kernel. After the process receives the event, it can choose to empty its assigned resources because it may not be called again. Process_event_continue: The event is sent by the kernel to a process that is waiting for a process_yield () execution. Process_event_msg: The event is sent to a process that has received a communication message. It is generally used for IP stacks to notify the process that a message has arrived, or it can be used to represent a common message between two processes. Process_event_exited: When a process is about to exit, the event is sent to all processes. Sending an event also sends a pointer to the process control block of the process that is exiting. When the event is received, the receiving process clears the state that will be assigned to the process to be exited. Process_event_timer: The event is sent to a process that has an event timer Etimer expired.

In addition to statically assigned event numbers, a process can allocate event identifiers that are greater than 128 between processes. The assigned event identifier is stored in a variable that the receiving process can use to match the event identifier.

Process Scheduler

The role of the process scheduler is to invoke processes. The process scheduler invokes a process by calling a function that implements the process thread. All processes in Contiki are designed to respond to events passed into the process, or to respond to polling requests by the process. The process Scheduler passes the event identifier and an opaque pointer to the process when the process is dispatched. The pointer is provided by the process caller and can be set to null (the event does not need to pass data). When a process requests polling, the data is not passed.

START Process

Process_start () begins a process. The purpose of this function is to set up the process control block, add the process to the kernel active process list, and then invoke the initialization code in the process thread. After the Process_start () is called, the process begins to run.

The process_start () function First makes a sensible check to see if the process already exists in the process chain list. If yes, indicates that the process has been run, theprocess_start () function returns directly.

After confirming that the process has not started running, the kernel joins the process to the list and sets the process control block. The state of the process is set to process_state_running, and the thread of the process is initialized by Pt_init () .

Finally, the kernel sends a synchronous event Process_event_initto the process and passes an opaque pointer to the process. The pointer is passed in by the process calling Process_start () to pass some information to the process to run. However, this pointer is generally set to null.

When a process receives its first event Process_event_init, the process executes the first part of the process thread. Typically, this section contains the initialization code that will run when the process starts.

exit process and kill process

There are two ways to exit a process: The process exits automatically and is killed by another process. The process exits when the function process_exit () is called or when the process thread executes to the Process_end () statement. A process can call the function Process_exit () to kill another process.

When a process exits (either actively exiting or being killed by another process), the Contiki kernel sends an event to notify the other process. This allows other processes to release the resources that the exit process occupies. For example, the UIP TCP/IP stack will shut down and remove all network connections owned by the exiting process. The event process_event_exited will be sent to all other processes in the same way as synchronous events.

When a process is killed by another process, the killed process receives the synchronization event Process_event_exit. This event notifies the process that will be killed and the process that can release the allocated resources, or any other process that will be exited.

The process is removed from the process list after the Contiki kernel sends an event notification that the process will be exited.

self-starting process

Contiki provides a mechanism to automatically run processes when the system starts up, or when the module containing the process is loaded. This mechanism is implemented by the self-starter module. This mechanism allows the module developer to tell the system what process is included in the module. When the module is removed from memory, the system can also be processed accordingly.

Since the startup process is saved in a linked list, the self-starter module uses the linked list to automate the process. The order in which the process starts is consistent with the order in which it is in the list.

There are two ways to start a process: When the system starts and when the module is loaded. All processes that need to be self-initiated at system startup must be included in a single, system-level linked list. This self-starting list is provided by the user and is typically in a user module. When this module is used as a loadable module, the list allows the system to know what process needs to be run when the module is loaded.

When a module is loaded, the module loader looks for a list of self-initiated processes and initiates a process in the list after the module is loaded into memory. When the module is about to be unloaded, the module loader uses the linked list to kill the process that was started during module loading.

In Contiki, the self-priming mechanism is the most common way to start a user process.

An example of a process

To more specifically discuss the process of working on processes, we now turn to an example of two processes. This routine not only shows what the process code looks like, but also shows the brother steps of the process life cycle. Here is a short "Hello,world"-style program, but this example is more granular.

An example of a process that receives an event and prints its number:

#include "contiki.h"  process (example_process, "example process"); Autostart_processes (&example_process);  Process_thread (example_process, Ev, data) {   process_begin ();    while (1) {     process_wait_event ();     printf ("Got Event number%d\n", Ev);   }    Process_end (); }

The above code is a complete example of a Contiki process. The process is declared, defined, and run automatically. We will analyze this example on a row-by-line basis.

In line 1th, we include the Contiki header file. Contiki.h contains all the header files required to implement the Contiki basic functions. In line 3rd, we define the process control block. The process Control block defines the name of the process Control block example_process and the literal, human-readable process name exampleprocesses.

After defining the process control block, we can use the variable name in other expressions. In line 4th, the statement autostart_processes () tells Contiki to start the process automatically when the system starts up (or when the module is loaded if it is compiled to a loadable module) example_process . The self-starting list consists of pointers to the process control block, so we need to add the & symbol to the example_process before it takes its address.

In line 6th, we begin to define the process thread. It contains the variable name of the process example_process and the variable EV that passes the event and data.

In line 8th, we use process_begin () to define the beginning of a process. This definition marks the beginning of the process thread. The code above the declaration statement will run (or rerun) every time the process is scheduled to run. The following code for the declaration statement executes according to the actual process thread control flow. In most cases, you don't need to put any code on top of Process_begin () .

In line 10th, we start the main loop of the process. As we said before, the Contiki process cannot contain a dead loop that never ends. In this case, however, our code is secure because the process will wait for the time. When a Contiki process waits for an event, it returns control to the Contiki kernel. During the process wait, the kernel will serve other processes.

On line 11th, the process waits for the event to occur. The expression process_wait_event () returns control to the Contiki kernel and waits for the kernel to pass events to the process. After the Contiki kernel passes events to the process, the code behindprocess_wait_event () is executed. After the process is awakened, the statement of Line 12th executes printf (). This line is just the event number that the print process received. If a pointer is passed at the same time, the pointer variable is data. However, in this case, we have ignored this pointer.

Line 15th of the statement Process_end () identifies the end of the process. Each Contiki process must contain process_begin () and process_end (). When executed to process_end () , the process ends and is removed from the process chain list in the kernel. However, in this case, because there is a dead loop between line 10th and line 13th, it never executes to process_end (). Only the system is turned off, or the process is killed by Process_exit () , it stops running.

A function that initiates a process and sends an event:

static char msg[] = "Data";  static void Example_function (void) {/   * Start "example process", and send it a NULL      pointer. *    /Process_start ( &example_process, NULL);     /* Send the process_event_msg EVENT synchronously      to ' Example PROCESS ' with a pointer to the ' message ' in the      array ' Msg '. *   /Process_post_synch (&example_process,                      process_event_continue, msg);     /* Send The process_event_msg EVENT asynchronously       to ' Example PROCESS ' with a pointer to the message in the      AR Ray ' msg '. *   /Process_post (&example_process,                process_event_continue, msg);    /* Poll "Example process". *   /Process_poll (&example_process);}

Two processes through events to complete an interaction. The function in the example above initiates a process and sends a synchronous event and polling request to it.


A process is the basic method of running an application in Contiki. A process consists of a Process control block and a process thread. The Process Control block contains real-time information that the process is running, and the process thread contains the code that the process actually executes. Protothread is a lightweight thread designed for memory-constrained systems.

In Contiki, code has two types of execution contexts: Shared (code used to not preempt other code) and preemption (preemption of shared code and return to control after execution). The normal process is always running in a shared context, and interrupts are run on the preemption contexts. The only process control function that can be called by preemptive mode is process_poll ().

The process communicates with other processes by sending an event implementation. Events are received when a process is started and introduced.

Contiki Tutorial--Process

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.