二十、Linux 進程與信號---非局部跳轉
阿新 • • 發佈:2018-06-02
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 變量是個全局變量,因為需從另一個函數中引用他。
- @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 進程與信號---非局部跳轉