二十、Linux 进程与信号---非局部跳转

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 }

  不进行优化编译后执行:

  

  

  优化编译后:

  

  

posted @ 2018-06-02 20:40  游戏进行中  阅读(367)  评论(0编辑  收藏  举报