PROCESS_YIELD()宏使用及過程分析


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

好了,就這么多。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM