1. 程式人生 > >二十、Linux 進程與信號---非局部跳轉

二十、Linux 進程與信號---非局部跳轉

out sizeof 成功 break Go AR i++ ken unistd.h

20.1 setjmp 和 longjmp 函數

20.1.1 函數介紹

#include <setjmp.h>
int setjmp(jmp_buf env);
  • 函數功能:設置非局部跳轉的跳轉點(設置跳轉點)
  • 返回值:直接調用返回0,若從 longjmp 調用返回則返回0
  • 這個函數會被執行兩次,一次是自己本身使用的時候返回0,另一次再調用 longjump 的時候,此函數再返回 longjmp 中的 val 值
#include <setjmp.h>
void longjmp(jmp_buf env, int val);
  • 函數功能:進行非局部跳轉,val 為返回值(具體完成跳轉,例如goto)
  • 參數:
    • @env:
      • 一個特殊類型 jmp_buf。這一數據類型是某種形式的數組,其中存放在調用 longjmp 時能用來恢復棧狀態的所有信息。一般,env 變量是個全局變量,因為需從另一個函數中引用他。
  • C程序缺乏異常處理的語法,可使用非局部跳轉處理C程序的異常
  • goto語句僅限於函數內部的跳轉,而 longjmp 不限於

20.1.2 例子

  process_jmp.c

  1 #include <setjmp.h>
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <stdio.h>
  5
#include <stdlib.h> 6 7 #define TOK_ADD 5 8 #define TOK_SUB 6 9 10 void do_line(char *line); 11 void cmd_add(void); 12 void cmd_sub(void); 13 int get_token(char *item);/* 獲取分割字符 */ 14 15 char *prompt = "cal:"; /* 命令行提示符 */ 16 jmp_buf env;/* 跳轉的 buf 結構 */ 17 18 int main(void
) 19 { 20 ssize_t size = strlen(prompt) * sizeof(char); 21 char buff[256]; 22 ssize_t len; 23 24 /* 設置跳轉點 */ 25 /* setjmp 第一次執行成功返回0,調用 longjmp 後此處再返回 非0值 */ 26 if(setjmp(env) < 0) { 27 perror("setjmp error"); 28 exit(1); 29 } 30 31 write(STDOUT_FILENO, prompt, size); 32 while(1) { 33 len = read(STDIN_FILENO, buff, 256); 34 if(len < 0) break; 35 36 buff[len - 1] = 0; 37 do_line(buff); 38 write(STDOUT_FILENO, prompt, size); 39 } 40 41 return 0; 42 } 43 44 void do_line(char *line) 45 { 46 int cmd = get_token(line); 47 48 switch(cmd) { 49 case TOK_ADD: 50 cmd_add(); 51 break; 52 case TOK_SUB: 53 cmd_sub(); 54 break; 55 default: 56 fprintf(stderr, "error command\n"); 57 } 58 59 } 60 61 void cmd_add(void) 62 { 63 int i = get_token(NULL); 64 int j = get_token(NULL); 65 printf("result is %d\n", i + j); 66 } 67 68 void cmd_sub(void) 69 { 70 int i = get_token(NULL); 71 int j = get_token(NULL); 72 printf("result is %d\n", i - j); 73 } 74 75 static int is_number(char *item) 76 { 77 int len = strlen(item); 78 int i; 79 80 for(i = 0; i < len; i++) 81 { 82 if(item[i] > 9 || item[i] < 0) 83 return 0; 84 } 85 86 return 1; 87 } 88 89 int get_token(char *line) 90 { 91 /* 92 * add 3 4 93 */ 94 char *item = strtok(line, " "); 95 96 if(line != NULL) { 97 if(!strcmp("add", item)) return TOK_ADD; 98 if(!strcmp("sub", item)) return TOK_SUB; 99 } else { 100 if(is_number(item)) { 101 int i = atoi(item); 102 return i; 103 } else { 104 fprintf(stderr, "arg not number\n"); 105 /* 如果輸入的參數不正常,則讓程序跳回到主函數執行下一次循環 */ 106 /* 進行非局部跳轉 */ 107 longjmp(env, 1);// 跳轉到 setjmp 處執行 108 } 109 } 110 }

  執行成功

  技術分享圖片

  如果將紅色部分註釋掉,會發現打印 reasult is xx 數字,xx數字 是一個隨機值,因為再 get_token 函數中,fprintf 後就沒有做退出也沒有做返回實際數字,那麽函數運行完畢後,就會返回一個隨機值來做加減運行,結果也就變為了一個隨機值。

20.2 非局部跳轉中,變量的使用

  編譯器優化編譯後:

  • 全局變量、靜態變量和 volatile(易矢變量)
    • 不能恢復到原始值
  • 寄存器變量
    • 可以恢復到原始值  
  • 自動變量潛在問題  
    • 優化編譯後可能會恢復
  • malloc 變量
    • 與編譯器優化有關,有的編譯器進行優化編譯會改變,有的編譯器不會,具體看編譯器優化    

  longjmp_val.c

 1 #include <setjmp.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <malloc.h>
 5 
 6 int g_val;
 7 
 8 jmp_buf env;
 9 
10 /*
11  * g_val:全局變量
12  * s_val:靜態變量
13  * a_val:自動變量,即局部變量
14  * r_val:寄存器變量
15  * m_val:通過 malloc 分配的變量
16  * v_val:易失變量
17  */
18 void fun1(int g_val, int s_val, int a_val, int r_val, int m_val, int v_val);
19 
20 void fun2();
21 
22 int main(void)
23 {
24     static int s_val;
25     int a_val;
26     register r_val;
27     int *m_val = (int *)malloc(sizeof(int));
28     volatile int v_val;
29 
30     g_val = 1;
31     s_val = 2;
32     a_val = 3;
33     r_val = 4;
34     *m_val = 5;
35     v_val = 6;
36 
37     int k = 0;
38 
39     if((k = setjmp(env)) < 0) {
40         perror("setjmp error");
41         exit(1);
42     } else if( k == 1) {
43         printf("after longjmp\n");
44         printf("g_val %d, s_val %d, a_val %d r_val %d, m_val %d v_val %d\n",
45                 g_val, s_val, a_val, r_val, *m_val, v_val);
46         exit(0);
47     }
48 
49     g_val = 10;
50     s_val = 20;
51     a_val = 30;
52     r_val = 40;
53     *m_val = 50;
54     v_val = 60;
55 
56     fun1(g_val, s_val, a_val, r_val, *m_val, v_val);
57 
58     return 0;
59 }
60 
61 
62 void fun1(int g_val, int s_val, int a_val, int r_val, int m_val, int v_val)
63 {
64     printf("before longjmp\n");
65 
66     printf("g_val %d, s_val %d, a_val %d r_val %d, m_val %d v_val %d\n",
67             g_val, s_val, a_val, r_val, m_val, v_val);
68 
69     fun2();
70 }
71 
72 void fun2()
73 {
74     longjmp(env, 1);
75 }

  不進行優化編譯後執行:

  技術分享圖片

  技術分享圖片

  優化編譯後:

  技術分享圖片

  技術分享圖片

二十、Linux 進程與信號---非局部跳轉