好吧,昨晚上研究了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的用法很奇特,不過是正確的。其原理,已經在上篇筆記寫明了。
好了,就這么多。