PROCESS_YIELD()宏使用及过程分析<contiki学习笔记之八>

好吧,昨晚上研究了switch()的底层实现原理--发现它并不是一般C语言教科书上那样所言,当然,这对于本身就非常熟悉汇编的同学来说,是小菜一碟。世界上,很多事情是巧合与必然的结合体,没有无缘无故的爱,也没有无缘无故的恨---我为啥会被一个switch给挡出去路?这个switch在contiki中又有何重要作用?且不回答这个问题,先来看看如何使用昨天晚上展开的PROCESS_YIELD()宏。

说明:这里就只是贴打印信息,分析打印信息了,不再贴分析过程中的代码了。

一,修改自己的hello-world.c文件内容如下:

 1 #include "contiki.h"
 2
 3 #include <stdio.h> /* For printf() */
 4 /*---------------------------------------------------------------------------*/
 5 PROCESS(hello_world_process, "Hello world process");
 6 PROCESS(test_process, "test process");
 7 //AUTOSTART_PROCESSES(&hello_world_process);
 8 AUTOSTART_PROCESSES(&hello_world_process, &test_process);
 9 /*---------------------------------------------------------------------------*/
10 PROCESS_THREAD(hello_world_process, ev, data)
11 {
12   printf("\n\ntest hello_process...\n\n");
13   PROCESS_BEGIN();
14   PROCESS_YIELD();
15
16   printf("Hello, world\n");
17
18   PROCESS_END();
19 }
20 /*---------------------------------------------------------------------------*/
21 PROCESS_THREAD(test_process, ev, data)
22 {
23  printf("\n\ntest test_process..\n\n");
24   PROCESS_BEGIN();
25
26   printf("Haha, only test\n");
27
28   PROCESS_END();
29 }

说明,上面代码就是在hello_process 这个process上添加了一个新的 test_process。这个test_process就是为了打印一个中国人的"Haha",不再是"Hello world"。

于是增加了第6行,以及第21~29行,修改了第8行。为了测试需要,在两个process函数体中,在PROCESS_BEGIN()之前,都添加了printf()语句。代码很简单,不做说明。

二,实现自己的contiki-main.c函数

这个就在 contiki-2.6/platform/native/ 下实现吧。只是把原来的contiki-main.c给备份了。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include "contiki.h"
 4
 5 int
 6 main(int argc, char **argv)
 7 {
 8
 9   process_init();
10
11   autostart_start(autostart_processes);
12
13   return 0;
14 }
15 

很简单了,初始化process,然后就自动启动hello-world.c里面的两个process.

<ps:同时,为了观察的更清楚,我去process.c/pt.h文件里也加入了一些printf(),比如一些函数中,我会加入printf("\n%s,%d\n",__func__,__LINE__);的代码,当然还有其他的一些printf()内容,不详述。所以,下面的打印中会多出一些内容。>

3,make,执行hello-world.native

 1 ./hello-world.native
 2
 3 process_init, 222
 4
 5 process_start,105
 6
 7 call_process, 187
 8
 9
10 test hello_process...
11
12
13 PT_YIELD_FLAG = 0
14 PT_EXITED = 2, PT_ENDED = 3, PROCESS_EVENT_EXIT = 131,PT_YIELDED = 1, ret = 1
15
16 process_start,105
17
18 call_process, 187
19
20
21 test test_process..
22
23 Haha, only test
24 process end: PT_YIELD_FLAG = 0
25 PT_EXITED = 2, PT_ENDED = 3, PROCESS_EVENT_EXIT = 131,PT_YIELDED = 1, ret = 3
26
27 exit_process,135
28 exit_process, 155, and call call_process()
29
30 call_process, 187
31
32
33 test hello_process...
34
35
36 PT_YIELD_FLAG = 1
37 Hello, world
38 process end: PT_YIELD_FLAG = 0
39 PT_EXITED = 2, PT_ENDED = 3, PROCESS_EVENT_EXIT = 131,PT_YIELDED = 1, ret = 3
40
41 exit_process,135
42 exit_process, 155, and call call_process()
43
44 call_process, 187
45
46 exit_process, 178 ok exit...
47
48 exit_process, 178 ok exit...

1,第三行---->main()中的process_init()执行。

2,第5行---->main()中的autostart_start(autostart_processes);执行。然后整个函数会在一个for()里面调用process_start()函数

3,第7行---->就是process_start()后,会牵涉出后面的process处理函数,这里已经到了call_process,意思是开始准备调用某个process了。

4,第10行--->在call_process()的作用下,我们自己的hello_process开始执行了。这里已经开始进入了hello_process的函数体了。

5,第13行--->检测出 (char) PT_YIELD_FLAG 的值为0。其实这已经执行到了hello_process中的 PROCESS_YIELD()宏了。这个宏在上文已经展开--如果PT_YIELD_FLAG 为0,会直接返回一个(PT_YIELDED = 1),而不会再接着执行下面的代码了。需要说明的是,PROCESS_YIELD()宏是位于PROCESS_BEGIN()宏后面,在PROCESS_BEGIN()中,已经将PT_YIELD_FLAG 初始化为 1了。但是在PROCESS_YIELD()中又被强制置0了。当然,PROCESS_YIELD还保存了当前所在行的行数,这是通过宏"__LINE__"来实现的。

6,第14行--->正如第5点所言,call_process在调用hello_process()函数后,因为PROCESS_YIELD()宏的作用,导致得到了一个返回值为 (PT_YIELDED =1)的结果。

7,第16行、第18行-->重新开了process_start ,call_process的执行。这其实是开始调用下一个process了。从这里可以看出,hello_process中的"Hello world"并没有执行,马上就把cpu的权利让给了下一个process--->类似于hello_process睡着了~~

8,第21行--->进入了test_process这个process的函数体。我在test_process中并没有再次使用PROCESS_YIELD()宏了,所以就顺风顺水的把test_process给执行完了。执行完毕后,把PT_YIELDED 写为0,这个动作是在 PROCESS_END()宏里完成。

9,第,27行--->test_process执行完毕,开始进入了exit阶段,准备释放自己的资源什么的,交出cpu控制权什么的。

10,第28行-->在exit阶段,test_process会把cpu控制权交给下一个相邻的process-->当然,最好的办法是启动这个process,于是可见,在exit阶段,call_process又一次被使用了。当然,test_process的邻process是hello_process,于是就直接启动了hello_process了。在这里有一点要说明,似乎test_process在exit的时候,仅仅是交出了cpu的控制权,但是是否真正exit了呢-->也就是所有的资源都释放了,不再占用了呢?

11,第30、33行,表明程序在一次进入了hello_process-->也即是,hello_process再一次开始执行,那么结果如何?

12,第36行,在PROCESS_YIELD()这个宏里面,检测到了PT_YIELD_FLAG 这个值为1,那就开始了下面代码的执行。需要说明的是,PROCESS_YIELD()里面有个case语句,它会和PROCESS_BEGIN()宏里面的switch匹配。而其工作原理,也其实类似于一个goto语句。这在上篇笔记已经说明了,不再赘述。

13,第37、39行,很无聊了,没必要再说。

14,第41行-->hello_process 准备exit了。

15,第42、44行,就是一个准备交出cpu控制权的过程了。但是貌似hello_process发现它的邻process是一个 NULL,于是一赌气,索性就全部exit了,包括控制权和资源。为嘛hello_process的邻process是一个NULL呢?因为process_list是一个链表,链表的终端指向了NULL,这在process_init()中就决定了它的命运。

16,第46、48行,exit了两次,这让我很惊讶,暂时没弄明白。等后面再补充。

ps: 原来准备在该笔记中,将整个process的流程再用一个流程图画一下,但由于打印的第46,48行,不知为啥会去执行两次,一时没明白,图片就暂时不画了,先研究下了。

贴一下,宏展开代码:

 1  static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data)
 2 {
 3     printf("\ntest hello process!\n");
 4     char PT_YIELD_FLAG = 1;
 5     if (PT_YIELD_FLAG) {;}
 6     switch((process_pt) -> lc) {
 7         case 0:
 8             do {
 9                 PT_YIELD_FLAG = 0;
10                 (process_pt)->lc = __LINE__;
11                 case __LINE__:
12                     if(PT_YIELD_FLAG == 0) {
13                         return PT_YIELDED;
14                     }
15             }while(0);
16
17         printf("Hello world ! \n");
18     };
19     PT_YIELD_FLAG = 0;
20     process_pt->lc = 0;
21     return PT_ENDED;
22 }

这个宏展开代码中,switch的用法很奇特,不过是正确的。其原理,已经在上篇笔记写明了。

好了,就这么多。

如果有小伙伴看出错误了,求指正,谢谢!一起交流,一起学习,一起进步。

Email:[email protected]

时间: 2024-10-03 13:40:07

PROCESS_YIELD()宏使用及过程分析<contiki学习笔记之八>的相关文章

简单的玩玩etimer &lt;contiki学习笔记之九&gt;

好吧,我承认etimer有点小复杂,主要是它似乎和contiki的process搅在一起,到处都在call_process.那就先搜搜contiki下的etimer的example看看,然后再试着写一个demo玩玩. 在写demo之前,先说说自己是怎么找到etimer 的example的文件的. 在core/sys/etimer.h 文件中,在描述etimer的数据结构的时候,作者显示的指出,如果要使用etimer,就必须先使用 etimer_set()这个函数进行一些工作,如图: 是的,数据结

contiki-main.c 中的process系列函数学习笔记 &lt;contiki学习笔记之六&gt;

说明:本文依然依赖于 contiki/platform/native/contiki-main.c 文件. ------------------------------------------------------------------------------------------------------------------------------------- 根据上一个笔记里面添加的printf()语句的打印信息提示,hello world 打印是在执行了 1 autostart_

简单的玩玩etimer &lt;contiki学习笔记之九 补充&gt;

这幅图片是对前面  <<contiki学习笔记之九>>  的一个补充说明. 简单的玩玩etimer <contiki学习笔记之九> 或许,自己正在掀开contiki process最后的一层面纱: 或许,还有一段路要走: 或许,已经掀开... --------------- 一切,都只是process:只有有了process,才会轮到etimer_process  发言,除非,抛却一切机制,裸机实现etimer... process,是什么? 一个链表,还是单向的,仅此

MyBatis MapperScannerConfigurer配置――MyBatis学习笔记之八

MyBatis MapperScannerConfigurer配置——MyBatis学习笔记之八 2012-09-02 20:01:42 标签:Spring MyBatis MapperScannerConfigurer bean默认命名 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://legend2011.blog.51cto.com/3018495/980150 在上一篇博文的示例中,我们在beans.xml中配置了stu

PROCESS_YIELD()宏和C语言的switch语句&lt; contiki学习笔记之七&gt;

写在前面:  按照main()函数的代码一行一行的分析,该是看到了 etimer_process 这个位置.但是etimer_process实现里的一个宏 PROCESS_YIELD()引出了很多故事,于是单独把整个宏的东西整理成笔记,贴出来,和学习contiki的伙伴分享. 在说这个宏之前,得先记下c 语言的switch()遭遇. switch()从表面上来看,或许应该是非常简单的问题--C语言的基本功吧.它的使用方式,按照常规来说,如下图所示: 好吧,那就贴一段常规的代码: 1 int ma

Unity3D学习笔记之八为场景添加细节(一)

这一系列教程以及素材均参考自人人素材翻译组出品的翻译教程<Unity游戏引擎的基础入门视频教程>,下载链接附在第二篇学习笔记中. 我花了30分钟做了一个中等大小的迷宫场景,不知道大家自己发挥,做的场景大小如何. 在完成场景之后,我们看到Hierarchy视图里面的东西已经满了,所以我们先来整理一下Hierarchy视图.创建一个空的游戏物体命名为Environment. 然后来到Hierarchy视图,先讲First Person Controller找到,挪到最上方,然后选中第一个物体,按住

Contiki 学习笔记:????process_run 解析

process_run用于处理系统所有needspoll标记为1的进程及处理事件队列的下一个事件.本文深入原码,详细分析,也包括do_poll和do_event函数. 一.运行process_run int main() { dbg_setup_uart(); usart_puts("Initialising\n"); clock_init(); process_init(); process_start(&etimer_process, NULL); autostart_st

struts2学习笔记之八(result)

strust2的开发步骤 编写一个发送请求的页面,表单或超链接 编写一个action类 1. 推荐实现action接口或继承actionsupport 2. 为所有请求参数,包括需要传到下一个页面显示的数据提供filed 并提供相应的setter和getter 3. 要有无参的构造器 4. 处理请求的方法,该方法不能有形参声明 5. 处理请求的返回值是String 在struts.xml文件配置action struts.xml配制方法 常量配置 包配置和命名空间配置 这种设计是为了企业应用的模

struts2学习笔记之八:Action中方法的动态调用

方法一:action名称+!+方法名称+后缀 Action类中增加addUser()和delUser()方法, package com.djoker.struts2; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.Action; public class UserAction { private String username; private String password; pri