Contiki-NG中的应用程序通常是使用Process抽象编写的。 进程建立在称为Protothreads的轻量级线程库的顶部。
进程定义
首先在源文件的顶部声明一个进程。 PROCESS()宏带有两个参数:一个是用于标识进程的变量,另一个是进程的名称。 在第二行中,我们告诉Contiki-NG,该过程应在系统启动后立即自动启动。 可以在此处指定多个进程,以逗号分隔。 如果AUTOSTART_PROCESSES()行不包含现有进程,则必须使用process_start()函数手动启动该进程。
#include "contiki.h" /* Main include file for OS-specific modules. */
#include <stdio.h> /* For printf. */
PROCESS(test_proc, "Test process");
AUTOSTART_PROCESSES(&test_proc);
下面是一个基本的的进程定义
PROCESS_THREAD(test_proc, ev, data)
{
PROCESS_BEGIN();
printf("Hello, world!\n");
PROCESS_END();
}
首先,PROCESS_THREAD()宏采用PROCESS()调用中指定的进程的标识符。 ev和data参数包含传入事件的值,以及指向事件参数对象的可选指针。 PROCESS_BEGIN()标记将开始执行过程的位置。 在大多数情况下,除变量定义外,程序员应避免将代码放在PROCESS_THREAD()主体中此语句上方。
对于我们的基本流程实现,我们不必关心任何事件处理,因为我们只执行一条语句,然后通过让其到达PROCESS_END()调用隐式退出流程。
事件和调度
Contiki-NG建立在基于事件的执行模型上,在该模型中,进程通常在告诉调度程序它们正在等待事件之前执行大量工作,从而暂停执行。 此类事件可能是计时器到期、传入的网络数据包或正在传递的串口消息。
contiki采用协同式进程调度,这意味着每个进程负责将控制权自愿交还给操作系统,而不去执行太长时间。 因此,应用程序开发人员必须确保将长时间运行的操作拆分为多个进程调度,以允许调度程序在上一次停止的位置恢复。
等待事件
通过在PROCESS_BEGIN()和PROCESS_END()之间区域中调用PROCESS_WAIT_EVENT_UNTIL(),可以将控制权交给调度程序,并且仅在事件到达时才恢复执行。 给出了一个条件作为PROCESS_WAIT_EVENT_UNTIL()的参数,必须满足此条件才能使进程在调用PROCESS_WAIT_EVENT_UNTIL()之后继续执行。 如果不满足该条件,则该过程将控制权交还给OS,直到传递新事件为止。
PROCESS_THREAD(test, ev, data)
{
/* An event-timer variable. Note that this variable must be static
in order to preserve the value across yielding. */
static struct etimer et;
PROCESS_BEGIN();
etimer_set(&et, CLOCK_SECOND); /* Trigger a timer after 1 second. */
while(1) {
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
etimer_reset(&et);
}
PROCESS_END();
}
暂停和退让
要自愿释放对调度程序的控制,可以调用PROCESS_PAUSE(),如下例所示。 然后,调度程序将传递所有排队的事件,然后立即调度已暂停的进程。
PROCESS_THREAD(test_proc, ev, data)
{
PROCESS_BEGIN();
while(have_operations_to_do()) {
do_some_operations();
PROCESS_PAUSE();
}
PROCESS_END();
}
相比之下,PROCESS_YIELD()会将控制权交还给调度程序,而不希望此后不久再进行调度。 相反,它将等待传入事件,类似于PROCESS_WAIT_EVENT_UNTIL(),但没有必需的条件参数。 可以通过调用process_poll()由外部进程或模块轮询已产生的进程。 要轮询使用变量test_proc声明的进程,可以调用process_poll(&test_proc);。 轮询进程将立即进行调度,并且将向其发送PROCESS_EVENT_POLL事件。
停止进程
一个过程可以通过三种方式停止:
- 通过允许到达并执行
PROCESS_END()语句,该过程隐式退出。 - 通过在
PROCESS_THREAD主体中调用PROCESS_EXIT(),显式退出该进程。 - 另一个进程通过调用
process_exit()终止该进程。
停止进程后,可以通过调用process_start()从头开始重新启动。
系统定义的事件
Contiki-NG使用基于系统定义的事件进行常见操作,如下所示。
| Event | ID | Description |
|---|---|---|
| PROCESS_EVENT_NONE | 0x80 | 无事件 |
| PROCESS_EVENT_INIT | 0x81 | 传递给正在开始的进程 |
| PROCESS_EVENT_POLL | 0x82 | 传递给正在轮询的进程. |
| PROCESS_EVENT_EXIT | 0x83 | 传递给正在退出的进程。 |
| PROCESS_EVENT_SERVICE_REMOVED | 0x84 | 未使用的. |
| PROCESS_EVENT_CONTINUE | 0x85 | 交付给从暂停中恢复执行时的进程。 |
| PROCESS_EVENT_MSG | 0x86 | 在发生传感器事件时传递给进程。 |
| PROCESS_EVENT_EXITED | 0x87 | Delivered to all processes about an exited process. |
| PROCESS_EVENT_TIMER | 0x88 | 当某个计时器到期时,将其交付给进程。 |
| PROCESS_EVENT_COM | 0x89 | Unused. |
| PROCESS_EVENT_MAX | 0x8a | 系统定义的事件的最大数量。 |
用户自定义的事件
还可以指定系统定义的事件未涵盖的新事件。 应该在适当的范围内定义和声明事件变量,以便该事件的所有预期用户都可以访问它。 基本情况是仅在单个模块中使用它,然后可以在模块顶部将其定义为静态。
static process_event_t my_app_event;
[...]
my_app_event = process_alloc_event();
请注意,不支持取消事件的方法。