Protothread:html
Lightweight, Stackless Threads in C 數組
C協程less
利用C語言的語法特性或者利用編譯器特性來完成上行文的切換,全部的thread共用一個堆棧,只是用2byte保存上下文。相似於協做式操做系統,由thread主動釋放CPU。設計原理可參照http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html 文件代碼可參照http://dunkels.com/adam/pt/index.html異步
一個典型的Protothread(如下簡稱pt)以下 PT_INIT(&ptex); PT_THREAD(pt_name)(struct *pt, void *param) { PT_BEGIN(pt); while(1) { PT_WAIT_UNTIL(pt, condition); } PT_END(pt); } 按照lc.h的定義 #define LC_INIT(s) s = 0; #define LC_RESUME(s) switch(s) { case 0: #define LC_SET(s) s = __LINE__; case __LINE__: #define LC_END(s) } 和pt.h的宏定義: #define PT_INIT(pt) LC_INIT((pt)->lc) #define PT_THREAD(name_args) char name_args #define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) {;} LC_RESUME((pt)->lc) #define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \ PT_INIT(pt); return PT_ENDED; } #define PT_WAIT_UNTIL(pt, condition) do {LC_SET((pt)->lc); if(!(condition)) {return PT_WAITING;}\ } while(0) 將上面代碼展開: &ptex->lc = 0; char pt_name(struct *pt, void *param) { char PT_YIELD_FLAG = 1; if(PT_TIELD_FLAG){;}; switch(pt->lc) { case 0: PT_BEGIN While(1) { do { Pt->lc = __LINE__; case __LINE__: If(! condition) { return PT_WAITING; } }while(0); PT_WAIT_UNTIL } } PT_END } 假設在main函數中一直call這個函數 main() { while(1) { pt_name(&ptex,NULL); pt_name1(&ptex1,NULL); } }
那麼即便,每次從__LINE__處退出了,下一次進入函數會再到__LINE__處執行,這個流程巧妙的利用__LINE__保存上下文,利用循環調用函數來完成調度。函數
基於上面的分析可知(基於switch-case):post
1. pt 中使用的局部變量,在發生調度後無效。由於調度是經過調用函數來完成。因此pt內要注意局部變量的使用spa
2. 由於上下文切換依賴的是switch-case,所以在pt內使用單獨的case語句會致使流程混亂,因此儘可能避免在pt中使用case。操作系統
3. pt的依賴於case的跌落特性,在展開後的代碼明顯違反代碼規範,並且會致使編譯器warning.設計
4. 發生上下文切換的條件是一個pt主動釋放CPU(從函數返回),而後另外一個pt開始執行(進入另外一個函數執行)rest
Contiki process:
Contiki是專門爲物聯網開發的操做系統,其core部分是基於pt來開發的,引入了基於event的調度機制,作成基於事件驅動的OS,因爲佔用資源極少,採用純C語言。所以能夠將其core方便的移植到低資源的嵌入式平臺上。代碼參見http://www.contiki-os.org/
Contiki core基本結構分析:
1. Process
Process 結構
Process基於pt擴展而來,process以單鏈表形式管理全部process
struct process {
struct process *next;
PT_THREAD((* thread)(struct pt *, process_event_t, process_data_t));
struct pt pt;
unsigned char state, needspoll;
};
next: 指向下一個process
thread:實際的process pt函數
pt: pt上下文
state: process狀態
PROCESS_STATE_NONE process退出後的狀態
PROCESS_STATE_RUNNING process 就緒,等待運行
PROCESS_STATE_CALLED process 本次已運行,等待下次調度
needspoll: poll標誌
process自己是基於event的,當有event觸發時一個process纔會工做,設置needspoll標誌,在無event觸發的狀況下也要執行process,執行一次process後該標誌會被清掉
對於51內核來講一個process佔用了3+3+2+1+1 = 10個字節
Process的形式
PROCESS(name, strname);
PROCESS_THREAD(name, ev, data)
{
PROCESS_BEGIN()
While(1)
{
PROCESS_WAIT_EVENT_UNTIL(c)
}
PROCESS_END()
}
Process的操做
void process_init(void)
初始化process管理數據和鏈表
process_start(struct process *p, const char *arg)
將process加入到process鏈表中,並調度一次
void process_exit(struct process *p)
退出process
void process_poll(struct process *p)
觸發一個指定process去poll一次
2. Event
contiki event默認支持32個event,以數組的形式管理,先進後出,所以有可能會出現最開始發生的event最後處理。對於同步的event,直接調用process的處理。對於異步event,先將event放到event數組內,在之後的調度中處理event。
Event的結構
struct event_data {
process_event_t ev;
process_data_t data;
struct process *p;
};
一個event佔用1+3+3=7個字節
ev:事件類型標識
PROCESS_EVENT_NONE
PROCESS_EVENT_INIT
PROCESS_EVENT_POLL
PROCESS_EVENT_EXIT
PROCESS_EVENT_SERVICE_REMOVED
PROCESS_EVENT_CONTINUE
PROCESS_EVENT_MSG
PROCESS_EVENT_EXITED
PROCESS_EVENT_TIMER
PROCESS_EVENT_COM
PROCESS_EVENT_MAX 自定義事件的起始值
data: event自帶數據
p: 事件發向那個process,process爲NULL的話就是廣播事件
Event的操做
process_event_t process_event_t process_alloc_event(void)
分配一個event
int process_post(struct process *p, process_event_t ev, process_data_t data)
異步post event,發送後當即返回,在之後的調度中處理event
void process_post_synch(struct process *p, process_event_t ev, process_data_t data)
同步post event,發送阻塞,直到event被process處理完成爲止
int process_nevents(void);
查詢還有多少個event未處理, process poll被看作是一個優先級高於其它event的特殊event。
3. Schedule
調度分爲兩個步驟,一是處理poll,二是處理event。
int
process_run(void)
{
/* Process poll events. */
if(poll_requested) {
do_poll();
}
/* Process one event from the queue */
do_event();
return nevents + poll_requested;
}
do_poll()
遍歷process list,將其中是poll的process都執行一遍,一次調度執行一次這個動做。
do_event()
去除event數組的最末尾的event,並交由對應的process處理,一次調度只能處理一個event。
call_process(struct process *p, process_event_t ev, process_data_t data)
process處理event的函數,當一個process處理了event後,將被設置爲PROCESS_STATE_CALLED狀態,那麼下次調度就不會被處理,將CPU時間可讓給其它的process。若是一個process的狀態時PROCESS_STATE_CALLED那麼在此次調度後會將狀態再設置爲PROCESS_STATE_RUNNING以從新獲得調度的權利。
4. Timer
contiki core包含有5中類型的timer,全部timer的根基是clock,都是從clock獲得時鐘,全部contiki core的移植就是實現clock爲系統提供基準便可。
timer
系統時鐘timer,以tick爲單位提供時間到期檢查功能
timer結構
struct timer {
clock_time_t start;
clock_time_t interval;
};
start :timer的開始時間
interval:timer持續時間
一個timer佔用內存的大小取決於clock_time_t的粒度
timer的操做
void timer_set(struct timer *t, clock_time_t interval)
設置一個timer
void timer_reset(struct timer *t)
reset timer,timer的啓動時間將被設置到timer到期的時間
void timer_restart(struct timer *t)
從當前時間開始重啓一個timer
int timer_expired(struct timer *t)
檢查timer是否到期
clock_time_t timer_remaining(struct timer *t)
檢查timer還有多久到期
stimer
秒 timer,以s爲單位提供時間到其餘檢查功能,功能相似於timer,只是以s爲單位
etimer
event timer,一個etimer必須綁定到一個process上,etimer到期後就發PROCESS_EVENT_TIMER到綁定的process上。etimer基於etimer_process和timer來實現的,etimer_process只響應兩類event
PROCESS_EVENT_EXITED和PROCESS_EVENT_POLL。PROCESS_EVENT_EXITED是通知一個綁定了etimer的process exit,對應的etimer須要被刪除。PROCESS_EVENT_POLL是讓etimer_process檢查是否有etimer到期。PROCESS_EVENT_POLL是由etimer_request_poll()產生,而在正常的etimer檢查狀況下etimer_process是不會本身產生PROCESS_EVENT_POLL的,因此須要調度call etimer_request_poll()來觸發etimer process檢查。
當etimer檢查到timer到期,會發生PROCESS_EVENT_TIMER到綁定的process,若是發送失敗,etimer_process會再本身call etimer_request_poll()來觸發一次檢查。若是發生成功了etimer就會從timer list中刪除,所以一個etimer啓動後到期一次就失效了。
etimer結構
etimer以鏈表的形式管理,並使用timer來實現
struct etimer {
struct timer timer;
struct etimer *next;
struct process *p;
};
timer: 到期檢查timer
next:指向下一個timer
p:指向綁定的process
所以一個etimer佔用的內存爲3+3+timer size
etimer操做
void etimer_set(struct etimer *et, clock_time_t interval)
設置並啓動一個etimer
void etimer_reset(struct etimer *et)
reset etimer,能夠觸發一個失效etimer重啓
void etimer_restart(struct etimer *et)
restart etimer,能夠觸發一個失效etimer重啓
void etimer_adjust(struct etimer *et, int td)
調整etimer時間
clock_time_t etimer_expiration_time(struct etimer *et)
獲取etimer到期的時間
clock_time_t etimer_start_time(struct etimer *et)
獲取etimer開始的時間
int etimer_expired(struct etimer *et)
檢查etimer是否到期
void etimer_stop(struct etimer *et)
中止一個etimer
void etimer_request_poll(void)
觸發etimer_process poll
int etimer_pending(void)
檢查是否還有etimer在工做
clock_time_t etimer_next_expiration_time(void)
獲取全部etimer的到期時間總和,能夠以此爲判斷在調度器內來決定是否要觸發etimer_process poll
ctimer
ctimer是以etimer爲基礎,以list形式管理。建立ctimer_process,當有etimer 到期後, ctimer_process將收到,PROCESS_EVENT_TIMER,process根據event data找到時那個ctimer,再執行ctimer的建立時的callback.
Ctimer也是和一個process綁定的,當執行callback時,current的process是這個callback綁定的process。
ctimer結構
struct ctimer {
struct ctimer *next;
struct etimer etimer;
struct process *p;
void (*f)(void *);
void *ptr;
};
next: 指向下一個ctimer
etimer: ctimer使用的etimer
p: 與ctimer綁定的process
f: ctimer的callback
ptr: ctimer callback的參數
ctimer的操做
void ctimer_init(void)
初始化ctimer process和鏈表
void ctimer_set(struct ctimer *c, clock_time_t t,void (*f)(void *), void *ptr)
建立並啓動一個ctimer
void ctimer_reset(struct ctimer *c)
void ctimer_restart(struct ctimer *c)
void ctimer_stop(struct ctimer *c)
int ctimer_expired(struct ctimer *c)
rtimer
須要平臺支持,在硬件timer裏面實現這個實時timer.