PROCESS_YIELD()宏和C語言的switch語句< contiki學習筆記之七>

      寫在前面:  按照main()函數的代碼一行一行的分析,該是看到了 etimer_process 這個位置。可是etimer_process實現裏的一個宏 PROCESS_YIELD()引出了不少故事,因而單獨把整個宏的東西整理成筆記,貼出來,和學習contiki的夥伴分享。ide

    在說這個宏以前,得先記下c 語言的switch()遭遇。函數

    switch()從表面上來看,或許應該是很是簡單的問題--C語言的基本功吧。它的使用方式,按照常規來講,以下圖所示:學習

好吧,那就貼一段常規的代碼:測試

 1 int main(void)
 2 {
 3     int k = 1;
 4     switch(k) {
 5         case 0: printf("good job!\n");break;
 6         case 1: printf("hello world!\n");break;
 7         default:break;
 8     }
 9     return 0;
10 }

很規矩的一段switch()的代碼,幾乎是全部C語言書上的教程。輸出結果就再也不說了。Hello world 嘛。spa

再來看一段:code

 1 int main(void)
 2 {
 3     int k = 1;
 4     switch(k) {
 5         case 0:
 6             do{
 7                 printf("good job !\n");
 8                 case 1:                     printf("Hello world!\n");
11                 }while(0);
12             };
13             return 0;
14 }

這段呢? 能編譯經過嗎?能執行嗎?或者,結果如何?blog

在QQ羣裏問了一些,衆說紛紜,其中有懷疑者,有否認者,還有猜想者。最難堪的是,有大牛知道這個程序的原理,而後直接讓我看C語言書..  教程

在這種鄙視下,果斷的使用如下這個命令:字符串

gcc -S test.c

生成了 test.s文件編譯器

打開以下:

 1     .file    "test.c"
 2     .section    .rodata
 3 .LC0:
 4     .string    "good job !"
 5 .LC1:
 6     .string    "Hello world!"
 7     .text
 8     .globl    main
 9     .type    main, @function
10 main:
11 .LFB0:
12     .cfi_startproc
13     pushq    %rbp
14     .cfi_def_cfa_offset 16
15     .cfi_offset 6, -16
16     movq    %rsp, %rbp
17     .cfi_def_cfa_register 6
18     subq    $16, %rsp
19     movl    $1, -4(%rbp)
20     movl    -4(%rbp), %eax
21     testl    %eax, %eax
22     je    .L3
23     cmpl    $1, %eax
24     je    .L4
25     jmp    .L2
26 .L3:
27     movl    $.LC0, %edi
28     call    puts
29 .L4:
30     movl    $.LC1, %edi
31     call    puts
32 .L2:
33     movl    $0, %eax
34     leave
35     .cfi_def_cfa 7, 8
36     ret
37     .cfi_endproc
38 .LFE0:
39     .size    main, .-main
40     .ident    "GCC: (GNU) 4.8.2 20131212 (Red Hat 4.8.2-7)"
41     .section    .note.GNU-stack,"",@progbits

      好吧,簡略的說下這個彙編代碼,其中 .LC0  .LC1裏面放了咱們要打印的字符串。第10行開始是main()的入口地址。而後下面的第26行.L3 和第29行.L4,就是分別打印了.LC0和.LC1裏面的字符串。

      像C語言同樣,從Main()開始看吧 <中間雜亂的就略過了>,

      第21行, testl 指令開始測試 %eax是正數負數仍是0,而後下一條  je  指令,在彙編中表示相等/爲0   的話就跳轉到某個地方去,這裏就跳轉到.L3裏面去。  

      第23行,cmpl 指令,就是檢測  %eax 寄存器的值是否爲 1,。如果,則跳轉到某個地方去,這裏就跳轉到.L4裏面去。

      第31行和36行,都call 了 puts函數,其實就是gcc 把printf()偷換成了puts吧。

      解釋的就這麼多,其餘無非就是push  pop的操做。這是彙編最愛乾的事情。

      另外就是,上面的解釋中,不斷的使用了 "跳轉" 一詞,那麼,還有一個關鍵字,在C語言中也是跳轉功能,那就是 goto。

     那麼,按照上面的思路來講,switch()語句的底層實現,其實就是一個goto原理:先是判斷一個值,而後根據這個值跳到某個地方去,這個地方用標籤標出來。那麼switch(k)這個時候就是在對值進行判斷,case 就是那個標籤,至於其中goto 被隱含了。通常的人不知道,可是編譯器知道---這就足夠了。

    一言以蔽之: switch() = 判斷 + Lable + goto. --->case語句隨便寫在switch(){}裏面的某個位置,都無所謂了(若是沒有break 和return的話)。

    那麼,這仍是教科書上的switch()嗎? 答案有待商榷。可是,按照這段彙編,咱們上面那段怪怪的C語言,結果有了,當K = 1的時候,"Hello world";當k = 0的時候,"good job"  "Hello world". 如今是case 0語句裏面套了一個case 1語句,如有興趣,能夠在case 1裏面再套一個case 2子句,而後把k = 2 看看結果如何....

 

好吧,switch()就先說在這裏。接下來看PROCESS_YIELD()宏的展開。

--------------------------------------------------------------------------------------------------

PROCESS_YIELD()宏

contiki/./core/sys/process.h:

1 #define PROCESS_YIELD()             PT_YIELD(process_pt)

PROCESS_YIELD 被 PT_YIELD替換掉:

PT_YIELD(pt)

contiki/./core/sys/pt.h

1  #define PT_YIELD(pt)                \
2    do {                      \
3      PT_YIELD_FLAG = 0;              \
4      LC_SET((pt)->lc);               \
5      if(PT_YIELD_FLAG == 0) {            \
6        return PT_YIELDED;            \
7      }                       \
8    } while(0)

先把其中的 LC_SET()宏也打開吧:

contiki/./core/sys/lc-switch.h

1  #define LC_SET(s) s = __LINE__; case __LINE__:

回溯一下,PROCESS_YIELD()宏被替換成這個樣子:

1             do {
2                 PT_YIELD_FLAG = 0;
3                 (process_pt)->lc = __LINE__;
4                 case __LINE__:
5                     if(PT_YIELD_FLAG == 0) {
6                         return PT_YIELDED;
7                     }        
8             }while(0);    

說明:

 PT_YIELD_FLAG 這個是在 PROCESS_BEGIN()宏中的產物,一個char 型變量,在PROCESS_BEGIN()中被初始化成了 1。固然在PROCESS_END()裏面也有它,並把它置爲0了。固然,在PROCESS_YIELD()這裏也有它。

(process_pt)->lc = __LINE__;  就是把程序當前行給保存下來了。<但並無保存現場>

case __LINE__:  這個確定要和switch()配合用,並且,當switch()的測試值爲這個 __LINE__的時候,就跳到這個case裏面去執行。

if(PT_YIELD_FLAG == 0) {
return PT_YIELDED;
}

這就沒解釋的必要了,無非就是在某種條件下,是繼續執行程序仍是直接返回去了。

 

但,就上面展開的8行代碼,鋪陳開了故事的全部脈絡....

 

關鍵字:  contiki  switch 彙編

相關文章
相關標籤/搜索