第5章 进程环境_非局部跳转

Posted 浅墨浓香

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第5章 进程环境_非局部跳转相关的知识,希望对你有一定的参考价值。

6. 非局部跳转

(1)setjmp和longjmp语句

头文件

#include<setjmp.h>

函数

int* setjmp(jum_buf env);

返回值

直接调用返回0,若从longjmp调用返回则返回非0值

功能

设置非局部跳转的跳转点

 

函数

void longjmp(jmp_buf env, int val);

功能

进行非局部转转,val为返回值

参数

env:一个特殊类型jmp_buf。这一数据类型是某种形式的数组,其中存放在调用longjmp时能用来恢复栈状态的信息(如,各寄存器的值;但不保存线程栈局部变量等数据)。一般,env变量是个全局变量,因为需要从另一个函数引用它。

备注

(1)C程序缺乏异常处理方法,可使用非局部跳转处理C程序的异常。

(2)goto语句仅限于函数内部跳转,而longjmp不限于

【编程实验】非局部跳转

//process_jmp.c

#include <setjmp.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TOK_ADD      5
#define TOK_SUB      6
char* const prompt = "cal: "; //命令行提示符

jmp_buf env;  //保存运行环境,非局部跳转用于函数间的跳转

//命令行格式如:add 3 5  或 sub 5 3
void do_line(char* line); //解析并执行命令行内容
void cmd_add(void);
void cmd_sub(void);
int get_token(char* line); //从命令行参数中获取标识符

int main(int argc, char* argv[])
{
    ssize_t size = strlen(prompt) * sizeof(char);
    char buff[256];
    ssize_t len = 0;

    //设置跳转点
    if(setjmp(env) < 0){ //直接调用返回0,从longjmp跳转来时,返回longjmp中指定的val值
                         //这里我们设定val为正值,小于0表示setjmp调用失败!
        perror("setjmp error");
        exit(1);
    }

    //输出提示符
    write(STDOUT_FILENO, prompt, size);
    while(1)
    {
        len = read(STDIN_FILENO, buff, 256);
        if(len < 0) break;

        buff[len -1] = 0;
        do_line(buff);

        write(STDOUT_FILENO, prompt, size);
    }

    return 0;
}


void do_line(char* line)
{
    int cmd = get_token(line);
    switch(cmd)
    {
    case TOK_ADD:
        cmd_add();
        break;
    case TOK_SUB:
        cmd_sub();
        break;
    default:
        fprintf(stderr, "error command\n");
    }
}

void cmd_add(void)
{
     int i = get_token(NULL);
     int j = get_token(NULL);

     printf("result: %d + %d = %d\n", i, j, i + j);
}

void cmd_sub(void)
{
     int i = get_token(NULL);
     int j = get_token(NULL);

     printf("result: %d - %d = %d\n", i, j, i - j);
}

static int is_number(char* item)
{
    int ret = 1;
    int len = strlen(item);
    int i = 0;
    
    for(i=0; i<len; i++) {
        ret = ret  && (0<= item[i]) && (item[i] <= 9);
        if (ret == 0) break;
    }

    return ret;
}

int get_token(char* line)
{
    //格式add 3 4
    //    sub 7 5
    char* item = strtok(line, " ");
    if(line != NULL)
    {
        if(!strcmp("add", item)) return TOK_ADD;
        if(!strcmp("sub", item)) return TOK_SUB;
    }else{
        if(is_number(item)){
            int i = atoi(item);
            return i;
        }else{
            fprintf(stderr, "arg not number\n");
            longjmp(env, 1);  //1表示跳到跳转点(setjmp)后,setjmp的返回值
        }
    }
}

 

(2)longjmp对各类变量的影响

 

变量类型

受影响情况

全局变量、静态变量和volatile变量

不能恢复到原始值

寄存器变量

可以恢复到原始值

自动变量

优化编译后可能会恢复

  ①存放在存储器中的变量将具有longjmp时的值而在cpu和浮点寄存器中的变量则恢复为调用setjmp时的值

  ②不进行优化时自动变量、寄存器变量、全局变量、静态变量和易失变量等都存放在存储器中(亦即忽略了对reg_var变量的register存储类说明)。

  ③而进行了优化后auto_var和reg_var都存放在寄存器中(即使auto_var并未声明为register),volatile变量则仍存放在存储器中

  ④全局、静态和易失变量不受优化的影响,在调用longjmp后,它们的值是最近所呈现的值。

  ⑤如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性

【编程实验】longjmp对变量的影响

//longjmp_var.c

#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

int global_var;

jmp_buf env;

int  show(int g_v, int s_v, int a_v, int r_v, int m_v, int v_v);
int  f1(int g_v, int s_v, int a_v, int r_v, int m_v, int v_v);
void f2();

int main(int argc, char* argv[])
{
    static int sta_var;
    int auto_var;
    register reg_var;
    int* heap_var = (int*)malloc(sizeof(int));
    volatile int vola_var;//易失变量

    global_var = 1; sta_var = 2; auto_var = 3;
    reg_var = 4; *heap_var = 5; vola_var = 6;

    int k = 0;
    if((k = setjmp(env)) < 0 ){
        perror("setjmp error");
    }else if(k == 1){
        printf("after longjmp:\n");
        show(global_var, sta_var, auto_var, reg_var, *heap_var, vola_var);       
        exit(0);
    }

    global_var = 10; sta_var = 20; auto_var = 30;
    reg_var = 40; *heap_var = 50; vola_var = 60;
    
    printf("before longjmp:\n");
    f1(global_var, sta_var, auto_var, reg_var, *heap_var, vola_var);
 
    return 0;
}

int  show(int g_v, int s_v, int a_v, int r_v, int m_v, int v_v)
{
    printf("  global: %d, static: %d, auto: %d, reg: %d, heap: %d vola: %d\n",
            g_v, s_v, a_v, r_v, m_v, v_v);
}

int  f1(int g_v, int s_v, int a_v, int r_v, int m_v, int v_v)
{
    show(g_v, s_v, a_v, r_v, m_v, v_v);
    f2();
}

void f2()
{
    longjmp(env, 1);
}
/*输出结果:
 [[email protected] 5.process]# gcc -o bin/longjmp_var src/longjmp_var.c
 [[email protected] 5.process]# bin/longjmp_var                         
 before longjmp:
   global: 10, static: 20, auto: 30, reg: 40, heap: 50 vola: 60
 after longjmp:
   global: 10, static: 20, auto: 30, reg: 40, heap: 50 vola: 60
 [[email protected] 5.process]# gcc -O -o bin/longjmp_var src/longjmp_var.c
 [[email protected] 5.process]# bin/longjmp_var                            
 before longjmp:
   global: 10, static: 20, auto: 30, reg: 40, heap: 50 vola: 60
 after longjmp:
   global: 10, static: 20, auto: 3, reg: 4, heap: 50 vola: 60
 */

以上是关于第5章 进程环境_非局部跳转的主要内容,如果未能解决你的问题,请参考以下文章

第5章 进程环境_进程的启动和终止

第5章 进程环境_环境表和环境变量

第5章 进程环境_进程结构(task_struct)

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

《第7章 进程环境》

第 3 章 进程