Contiki 調度內核不徹底介紹

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.

相關文章
相關標籤/搜索