struct process {
struct process *next; // 鏈表指針,串聯所有進程
#if PROCESS_CONF_NO_PROCESS_NAMES
#define PROCESS_NAME_STRING(process) ""
#else
const char *name; // 進程名稱(調試用)
#define PROCESS_NAME_STRING(process) ((process)->name)
#endif
PT_THREAD((* thread)(struct pt *, // 協程入口函數指針
process_event_t,
process_data_t));
struct pt pt; // Protothread 控制塊
unsigned char state; // 狀態:NONE/RUNNING/CALLED
unsigned char needspoll; // 輪詢標誌(優先級提示)
};
`next` 指針將所有進程串聯為單向鏈表,內核遍歷鏈表進行調度。`PROCESS_CONF_NO_PROCESS_NAMES` 宏可裁剪名稱字段以節省 RAM(極受限平台關鍵)。
PT_THREAD 展開為 `char (* thread)(struct pt *, process_event_t, process_data_t)`:
#define PROCESS_STATE_NONE 0 // 未運行/已結束
#define PROCESS_STATE_RUNNING 1 // 正在執行
#define PROCESS_STATE_CALLED 2 // 被調用中(避免重入)
`needspoll`:置 1 表示該進程有事件待處理,調度器優先輪詢,實現**事件驅動**而非純時間片輪轉。
PROCESS_THREAD(my_process, ev, data) {
PROCESS_BEGIN(); // 展開為 switch(pt->lc) { case 0:;
static struct etimer timer;
etimer_set(&timer, CLOCK_SECOND*2);
while(1) {
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer));
printf("Hello every 2 seconds\n");
etimer_reset(&timer);
}
PROCESS_END(); // 展開為 return 0; } }
}
關鍵宏:
void process_run(void) {
struct process *p = process_list;
while(p) {
if (p->state == PROCESS_STATE_RUNNING ||
(p->needspoll && process_needevents(p))) {
p->state = PROCESS_STATE_CALLED;
p->thread(&p->pt, PROCESS_EVENT_POLL, NULL); // 驅動協程
p->state = PROCESS_STATE_RUNNING;
}
p = p->next;
}
}
極簡調度:無優先級隊列、無時間片、無搶佔,**協作式輪詢**,適合極低功耗、單棧嵌入式場景。
| 優勢 | 劣勢 |
|---|---|
| 極低 RAM 佔用(無獨立棧) | 無法在阻塞調用中 yield(如 socket 讀寫) |
| 代碼簡潔,無竞態條件 | 長計算任務會阻塞整個系統 |
| 天然適合事件驅動模型 | 局部變量不跨 yield 保持,易犯錯 |
Contiki 的 process 機制是**針對嚴重資源受限設備的優雅妥協**,理解其協程本質與局限,是編寫正確 Contiki 應用的前提。