contiki/platform/linux/conkiki-main.c
这里是zipgateway主入口
int
main(int argc, char** argv)
{
signal(SIGINT,exit_handler );
signal(SIGTERM,exit_handler );
signal(SIGHUP,exit_handler );
//autostart_start启动由AUTOSTART_PROCESSES(&zip_process);定义的自动启动线程zip_process
autostart_start(autostart_processes);
}
主Z/IP进程此进程启动所有其他需要的进程
PROCESS(zip_process, "ZIP process");
//定义一个数组,存放程序中自动运行的线程,并将zip_process添加到数组中
//contiki初始化完成后将自动运行该线程
AUTOSTART_PROCESSES(&zip_process);
///////////////////////////////////////////////////////////////////////////////////////////
contiki/platform/linux/conkiki-main.c
void exit_handler(int num) {
if (num == SIGHUP) {
process_post(&zip_process,ZIP_EVENT_RESET,0);
}
process_post(PROCESS_BROADCAST,PROCESS_EVENT_EXIT,0);
}
//////////////////////////////////////
contiki/core/sys/process.c
int
process_post(struct process *p, process_event_t ev, process_data_t data) CC_REENTRANT_ARG
{
static process_num_events_t snum;
if(PROCESS_CURRENT() == NULL) {
PRINTF("process_post: NULL process posts event %d to process '%s', nevents %d\n",
ev,PROCESS_NAME_STRING(p), nevents);
} else {
PRINTF("process_post: Process '%s' posts event %d to process '%s', nevents %d\n",
PROCESS_NAME_STRING(PROCESS_CURRENT()), ev,
p == PROCESS_BROADCAST? "": PROCESS_NAME_STRING(p), nevents);
}
snum = (process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;
events[snum].ev = ev;
events[snum].data = data;
events[snum].p = p;
++nevents;
return PROCESS_ERR_OK;
}
////////////////////////////////////////
src/ZIP_Router.c
PROCESS(zip_process, "ZIP process");
contiki/core/sys/process.h
#define PROCESS(name, strname) \
PROCESS_THREAD(name, ev, data); \
struct process name = { NULL, strname, \
process_thread_##name }
contiki/core/sys/process.h
#define PROCESS_THREAD(name, ev, data) \
static PT_THREAD(process_thread_##name(struct pt *process_pt, \
process_event_t ev, \
process_data_t data))
综合上面两个
#define PROCESS(name, strname) \
static PT_THREAD(process_thread_##name(struct pt *process_pt, \
process_event_t ev, \
process_data_t data)); \
struct process name = { NULL, strname, \
process_thread_##name }
又有#define PT_THREAD(name_args) char name_args
PROCESS(zip_process, "ZIP process");
也就变成了
static char process_thread_zip_process(struct pt *process_pt, \
process_event_t ev, \
process_data_t data);
struct process zip_process = { NULL, "ZIP process", \
process_thread_zip_process }
//////////////////////////////////////////////////////////////////
//声明一个线程Process,线程名为:zip_process
//线程说明文字:"ZIP process"
src/ZIP_Router.c
PROCESS(zip_process, "ZIP process");
//定义一个数组,存放程序中自动运行的线程,并将zip_process添加到数组中
//contiki初始化完成后将自动运行该线程
src/ZIP_Router.c
AUTOSTART_PROCESSES(&zip_process);
执行由AUTOSTART_PROCESSES(&zip_process);定义的多个自动运行的线程
autostart_start(autostart_processes);
//线程定义,参数分别为:参数名,事件ev,事件附带的数据data
src/ZIP_Router.c
PROCESS_THREAD(zip_process, ev, data){
//所有线程的实现都是以PROCESS_BEGIN()开始
PROCESS_BEGIN()
.........
//所有线程的实现都是以 PROCESS_END()结束
PROCESS_END()
}
两个都是阻塞当前process线程直到一个事件发生,不过后者要求 c必须为真。
PROCESS_WAIT_EVENT()
PROCESS_WAIT_EVENT_UNTIL(c)
//////////////////////////////////////////////
发送一个事件给给另一个线程
post一个同步事件到一个或者多个processs ,处理此事件的操作将会在目标process被内核调度的时候被处理。一个事件可以被广播到所有的process,此时,所有的process都会被调度去处理这个事件。
参数p ,可以是目标process 或者PROCESS_BROADCASE。
int process_post(struct process *p,process_event_t ev,process_data_t data)
同步是做程序必须等待该事件完成后才能继续执行下一行代码
void process_post_synch(struct process *p, process_event_t ev, process_data_t data)
异步是指程序不必等待该事件完成就能执行下一行代码,且事件具体完成的时间是事先无法预料的
函数 process_post 用于向线程投递一个异步事件:
int process_post(struct process *p, process_event_t ev, process_data_t data)
int process_post(struct process *p, process_event_t ev, process_data_t data)
{
static process_num_events_t snum;
if(nevents == PROCESS_CONF_NUMEVENTS) {
// 事件的缓冲队列满了,返回一个错误码给调用线程
return PROCESS_ERR_FULL;
}
// fevent 是事件缓冲队列中缓存的第一个元素的索引
// nevents 是事件缓存队列中已缓存的元素的个数
// fevent + nevents 就表示缓存队列中已缓存数据(最后一个元素)的下一个索引,
// 也就是用来存储此次事件控制结构的索引
// 对 PROCESS_CONF_NUMEVENTS 进行求模运算是因为缓冲队列是环形的
snum = (process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;
// 将事件的控制结构信息保存到该索引所对应的内存空间里面。
events[snum].ev = ev;
events[snum].data = data;
events[snum].p = p;
// 缓冲队列中元素已缓冲元素个数递增 1
++nevents;
return PROCESS_ERR_OK;
}
////////////////////////////////////////////
让时间事件感知时钟的变化 一般在时钟中断里面,当tick增加的时候,
将本线程(事件定时器线程)设置为高优先级
调度器会优先再次调用本线程遍历链表
void etimer_request_poll(void)
查看是否有有过期的时间事件
void etimer_pending(void)
得到下一个时间事件流逝的时间
clock_timer_t etimer_next_expiration_time(void)
启动一个process
void process_start(struct process *p ,char *arg)
终止一个process
void process_exit(struct process *p)
当一个process poll一个事件时会执行这个操作p,但是必须在PROCESS_BEGIN()之前声明。
PROCESS_POLLHANDLER(p);
当一个process退出的时候,会执行这个操作p,但是必须在PROCESS_BEGIN()之前声明。
PROCESS_EXITHANDLER(p);
分配一个全局的事件号。contiki中,事件号大于128的是全局事件号,可以从一个process中post到另一个process中
process_event_t process_alloc_event(void)
运行一次系统 --调用poll 函数 并且处理一个事件
这个函数应该被main()函数反复的调用来真正的运行contiki 操作系统,它会调用需要轮询的服务函数,然后处理一个事件,这个函数将会返回在事件队列里面等待处理的事件个数,以便当队列里面没有等待事件时,可以让cpu休眠。
int process_run(void)
阻塞当前进程.也就是当前进程放弃running,放弃cpu使用权
PROCESS_YIELD();
阻塞当前运行的process 直到condition为真
PROCESS_YIELD_UNTIL(c)
Contiki 维护了一个全局的线程链表 struct process *process_list,所有被启动的线程都将加入到这个线程链表中
全局变量 struct process *process_current,它始终指向当前正在被调度的线程
Contiki 中的线程有三种状态:
PROCESS_STATE_NONE:无效态。表示一个线程没有被启动,或者已经被退出。处于这种状态的线程不在线程链表中。
PROCESS_STATE_CALLED:执行态。表示一个线程正在被调度,即正在执行线程的执行实体(入口函数)。处于这种状态的线程在线程链表中。
PROCESS_STATE_RUNNING:可执行态(就绪态)。表示线程处于可执行态,即线程位于线程链表中,等待某一个时刻被调度、执行
在 Contiki 中,线程有两个优先级:
普通优先级。线程结构体 struct process 中的成员 neeedspoll 为 0 的线程。
高优先级。线程结构体 struct process 中的成员 neeedspoll 为 1 的线程。
/////////////////////////////////////////////
通过 process_poll 函数可以将一个线程设置为高优先级的线程。故而此线程会被轮询
poll 的本意是轮询,但是在 Contiki 中所有 needspoll 被设为 1 的线程会比一般线程先被轮询,所以直接将 process_poll 叫做设置线程的优先级,这样更容易理解。
void process_poll(struct process *p)
全局变量 poll_requested,用来表示当前所有线程中是否有高优先级的线程。poll_request 为 1 表示有高优先级线程;否则,无高优先级线程
poll 的本意是轮询,但是在 Contiki 中所有 needspoll 被设为 1 的线程会比一般线程先被轮询,所以直接将 process_poll 叫做设置线程的优先级,这样更容易理解。
contiki/core/sys/process.c
void
process_poll(struct process *p)
{
if(p != NULL) {
if(p->state == PROCESS_STATE_RUNNING || p->state == PROCESS_STATE_CALLED) {
// 现将线程的 needspoll 成员置为 1
p->needspoll = 1;
// 再将全局变量 poll_requested 置为 1,以告诉线程的调度器
// 现在有线程处于高优先级状态。然后调度器在下次调度线程时,会
// 优先调度高优先级的线程
poll_requested = 1;
}
}
}
///////////////////////////////////////
函数 process_start() 用于启动一个已经创建的线程
contiki/core/sys/process.c
void
process_start(struct process *p, process_data_t data)
{
struct process *q;
// 如果线程已经在全局线程链表中(即已经被启动),直接返回
for(q = process_list; q != p && q != NULL; q = q->next);
if(q == p) {
return;
}
// 将该线程加入到线程链表的头部
p->next = process_list;
process_list = p;
// 设置线程的状态为可执行态
p->state = PROCESS_STATE_RUNNING;
// 初始化线程的上下文。详细信息请参考 线程切换 一节。
PT_INIT(&p->pt);
PRINTF("process: starting '%s'\n", PROCESS_NAME_STRING(p));
// 先线程投递一个同步事件 PROCESS_EVENT_INIT
// 关于投递事件,请参考 初始事件 一节
process_post_synch(p, PROCESS_EVENT_INIT, data);
}
//////////////////////////////////
函数 call_process() 才会真正去执行一个线程
contiki/core/sys/process.c
static void
call_process(struct process *p, process_event_t ev, process_data_t data)
{
int ret;
#if DEBUG
if(p->state == PROCESS_STATE_CALLED) {
printf("process: process '%s' called again with event %d\n", PROCESS_NAME_STRING(p), ev);
}
#endif /* DEBUG */
// 如果线程是处于可执行态,且线程的入口函数不为 NULL,则执行该线程的执行实体(入口函数)。
if((p->state & PROCESS_STATE_RUNNING) && p->thread != NULL) {
PRINTF("process: calling process '%s' with event %d\n", PROCESS_NAME_STRING(p), ev);
// 将 process_current 指向即将被调度的线程
process_current = p;
// 设置线程的状态为“正在被执行态”
p->state = PROCESS_STATE_CALLED;
// 调用该线程的执行实体,即切换到该线程去执行
ret = p->thread(&p->pt, ev, data);
// 代码走到这里,说明已经执行完 p->thread 这个线程实体了。在 p->thread 这个线程实体内部,
// 会返回一个返回代码,表示该线程执行实体是由于什么原因而返回。关于这个返回状态,更详细的信
// 息请参考 线程切换 一节。
if(ret == PT_EXITED || ret == PT_ENDED || ev == PROCESS_EVENT_EXIT) {
// 根据线程入口函数的返回值,判断是否要将该线程从线程链表中删除
// exit_process() 会对线程做一些清理工作,然后将其从线程链表中删除
exit_process(p, p);
} else {
// 将线程的状态由“正在被执行态”切换回“等待被执行态”
p->state = PROCESS_STATE_RUNNING;
}
}
}
///////////////////////////////
Contiki 中,线程的调度策略体现在 process_run() 这个函数中:
contiki/core/sys/process.c
int
process_run(void)
{
// 如果有线程被置为高优先级,则先调度这些线程
if(poll_requested) {
do_poll();
}
// 再调度一个普通线程
do_event();
// nevents 表示当前系统还有多少个事件需要处理,更多信息请参考 初识事件 一节。
return nevents + poll_requested;
}
/////////////////////////////////
处理高优先级线程
contiki/core/sys/process.c
do_poll() 这个函数将线程链表中所有需要高优先级的线程都执行了之后才返回的
do_poll():
static void
do_poll(void)
{
struct process *p;
poll_requested = 0;
// 轮询线程链表中的所有线程,如果该线程为高优先级的线程,则执行该线程
for(p = process_list; p != NULL; p = p->next) {
if(p->needspoll) {
p->state = PROCESS_STATE_RUNNING;
p->needspoll = 0;
// 调用 call_process 调度/执行该线程
call_process(p, PROCESS_EVENT_POLL, NULL);
}
}
}
/////////////////////////////////////
函数 add_timer() 用于将一个事件定时器添加到事件定时器链表中:
static void add_timer(struct etimer *timer)
{
struct etimer *t;
etimer_request_poll();
if(timer->p != PROCESS_NONE) {
// 如果定时器已经在链表中,查找到该定时器,然后重新绑定线程
for(t = timerlist; t != NULL; t = t->next) {
if(t == timer) {
timer->p = PROCESS_CURRENT();
update_time();
return;
}
}
}
// 代码走到这里,说明定时器不在链表中,
// 为定时器绑定当前线程
timer->p = PROCESS_CURRENT();
// 将定时器加入到链表的表头
timer->next = timerlist;
timerlist = timer;
update_time();
}
/////////////////////////////////
事件定时器线程只处理两类事件:PROCESS_EVENT_EXITED 和 PROCESS_EVENT_POLL。如果接收到投递过来的其它事件,直接返回(PROCESS_YIELD() 会保存当前线程的上下文,然后退出当前线程)
PROCESS_THREAD(etimer_process, ev, data)
{
struct etimer *t, *u;
PROCESS_BEGIN();
timerlist = NULL;
while(1) {
PROCESS_YIELD();
if(ev == PROCESS_EVENT_EXITED) {
// 有线程将要退出了,它向内核中的所有线程都投递了一个 PROCESS_EVENT_EXITED 事件
// 以通知所有的线程(如果需要)做清理工作。
// 当线程退出时,当它向所有线程投递 PROCESS_EVENT_EXITED 事件时,所绑定的 data
// 指向的该线程自己。
// 关于线程退出,请参考后续的《线程退出》一节。
struct process *p = data; // 指向将要退出的线程。
while(timerlist != NULL && timerlist->p == p) {
// 先将定时器链表的前面连续的若干个绑定了需要退出线程的定时器从链表中删除
timerlist = timerlist->next;
}
// 再遍历整个链表,当查找到有定时器所绑定的线程就是需要退出的那个线程时,
// 将该定时器从链表中删除
if(timerlist != NULL) {
t = timerlist;
while(t->next != NULL) {
if(t->next->p == p) {
t->next = t->next->next;
} else
t = t->next;
}
}
continue;
} else if(ev != PROCESS_EVENT_POLL) {
continue;
}
// 代码走到这里,说明本线程接收到的事件为 PROCESS_EVENT_POLL。
again:
u = NULL;
// 遍历这个链表,查看是否有定时器到期了
for(t = timerlist; t != NULL; t = t->next) {
if(timer_expired(&t->timer)) {
// 如果有定时器到期了,则向该定时器绑定的线程投递一个定时器事件 PROCESS_EVENT_TIMER。
if(process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK) {
// 如果定时器事件投递成功
// 先解除定时器绑定的线程
t->p = PROCESS_NONE;
// 再将该定时器从链表中删除
if(u != NULL) {
u->next = t->next;
} else {
timerlist = t->next;
}
t->next = NULL;
update_time();
goto again;
} else {
// 如果定时器事件投递不成功,将本线程(事件定时器线程)设置为高优先级
// 调度器会优先再次调用本线程遍历链表。
etimer_request_poll();
}
}
// u 表示在链表中到期定时器的前一个定时器
u = t;
}
}
PROCESS_END();
}
//////////////////////////////////////////
Contiki 中的调度器使用 do_event() 函数处理缓冲在事件缓冲队列中的事件
函数 do_event() 只从事件的缓冲队列中取出了一个事件,然后投递给对于的线程,即 do_event() 每次只处理一个事件
static void do_event(void)
{
static process_event_t ev;
static process_data_t data;
static struct process *receiver;
static struct process *p;
// 如果缓冲队列中缓冲了数据,处理之
if(nevents > 0) {
// 取出缓冲队列中的第一个数据,即索引 fevent 处存放的数据
ev = events[fevent].ev;
data = events[fevent].data;
receiver = events[fevent].p;
// 取出数据后,fevent + 1
// 对 PROCESS_CONF_NUMEVENTS 进行求模运算是因为缓冲队列是环形的
fevent = (fevent + 1) % PROCESS_CONF_NUMEVENTS;
// 取出数据后,缓冲队列缓冲的总元素个数递减 1
--nevents;
// Contiki 中定义了一个特殊的线程,叫做广播线程,即 PROCESS_BROADCAST
// 当向该线程投递事件时,实际上相当于给所有的线程都投递了一个事件
if(receiver == PROCESS_BROADCAST) {
// 如果接收线程是广播线程,遍历线程链表,依次调用所有线程
for(p = process_list; p != NULL; p = p->next) {
if(poll_requested) {
// 如果有高优先级的线程,则先调度高优先级的线程
do_poll();
}
// 调用线程
call_process(p, ev, data);
}
} else {
if(ev == PROCESS_EVENT_INIT) {
// 如果该事件是一个初始化事件,先修改该线程的状态为可执行态
receiver->state = PROCESS_STATE_RUNNING;
}
// 调用线程
call_process(receiver, ev, data);
}
}
//////////////////////////////////////////////////////
阅读(7788) | 评论(0) | 转发(0) |