进程环境之setjmp和longjmp函数
在C中,goto语句是不能跨越函数的,而执行这样跳转功能的是函数setjmp和longjmp。这两个函数对于处理发生在深层嵌套函数调用中的出错情况是非常有用的。
setjmp和longjmp函数也称为非局部goto,非局部指的是,这不是由普通C语言goto语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一函数中。
#include <setjmp.h> int setjmp( jmp_buf env );
返回值:若直接调用则返回0,若从longjmp调用返回则返回非0值 void longjmp( jmp_buf env, int val );
图7-4 调用cmd_add后的各个栈帧(函数调用关系为:main先调用do_line,do_line又调用cmd_add)
在希望返回到的位置调用setjmp,假定此位置在main函数中,因为我们是直接调用该函数,所以其返回值为0。setjmp参数env的类型是一个特殊类型jmp_buf。这一数据类型是某种形式的数组,其中存放:在调用longjmp时能用来恢复栈状态的所有信息。因为需要在另一个函数中引用env变量,所以规范的处理方式是将env变量定义为全局变量。
当检查到一个错误时,例如在cmd_add函数中,则以两个参数调用longjmp函数。第一个就是在调用setjmp时所用的env;第二个参数是具有非0值的val,它将成为从setjmp处返回的值。使用第二个参数的原因是对于一个setjmp可以有多个longjmp。例如,可以在cmd_add中以val为1调用longjmp,也可在do_line中以val为2调用longjmp。在setjmp的返回值就会是1或2,通过测试返回值就可以判断造成返回的longjmp是在cmd_add还是在do_line中。
我们假定在main中调用了setjmp(jmpbuffer),在cmd_add中调用了longjmp(jmpbuffer, 1)为例进行后续说明。
...
#include <setjmp.h>
...
jmp_buf jmpbuffer; int
main(void)
{
...
if( setjmp(jmpbuffer) != 0 )
printf("error");
...
} ...
void
cmd_add(void)
{
...
if( ... ) /* an error has occurred */
longjmp( jmpbuffer, 1 );
...
}
执行main时,调用setjmp,它将所需的信息记入变量jmpbuffer中并返回0。然后调用do_line,它又调用cmd_add,假定在其中检查到一个错误。在cmd_add中调用longjmp之前,栈的形式如图7-4所示。但是longjmp使栈反绕(rewind to)到执行main函数时的情况,也就是抛弃了cmd_add和do_line的栈帧(见图7-5)。调用longjmp造成main中setjmp的返回,但是,这一次的返回值是1(longjmp的第二个参数)。
图7-5 调用longjmp后的栈帧(f1代表do_line、f2代表cmd_add)
1、自动、寄存器和易失变量
调用longjmp时,大多数实现并不回滚自动变量和寄存器变量的值,而所有标准则说它们的值是不确定的。如果你有一个自动变量,而又不想使其值回滚,则可定义其为具有volatile的属性。声明为全局或静态变量的值在执行longjmp时保持不变。
程序清单7-6 longjmp对各类变量的影响
[root@localhost apue]# cat prog7-6.c
#include "apue.h"
#include <setjmp.h> static void f1(int, int, int, int);
static void f2(void); static jmp_buf jmpbuffer;
static int globval; int
main(void)
{
int autoval;
register int regival;
volatile int volaval;
static int statval; globval = 1;
autoval = 2;
regival = 3;
volaval = 4;
statval = 5; if(setjmp(jmpbuffer) != 0)
{
printf("after longjmp:\n");
printf("globval = %d, autoval = %d, regival = %d,"
"volaval = %d, statval = %d\n", /* ISO C的字符串连接功能 */
globval, autoval, regival, volaval, statval);
exit(0);
}
/*
* Change variables after setjmp, but before longjmp.
*/
globval = 95;
autoval = 96;
regival = 97;
volaval = 98;
statval = 99; f1(autoval, regival, volaval, statval); /* never returns */
exit(0);
} static void
f1(int i, int j, int k, int l)
{
printf("in f1():\n");
printf("globval = %d, autoval = %d, regival = %d,"
"volaval = %d, statval = %d\n", globval, i, j, k, l);
f2();
} static void
f2(void)
{
longjmp(jmpbuffer, 1);
}
如果以不带优化和带优化选项对此程序分别进行编译,然后运行它们,则得到的结果是不同的:
[root@localhost apue]# cc -o prog7-6 prog7-6.c 不进行任何优化的编译
[root@localhost apue]# ./prog7-6
in f1():
globval = 95, autoval = 96, regival = 97,volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 96, regival = 97,volaval = 98, statval = 99
[root@localhost apue]# cc -o prog7-6 -O prog7-6.c 进行全部优化的编译
[root@localhost apue]# ./prog7-6
in f1():
globval = 95, autoval = 96, regival = 97,volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 2, regival = 3,volaval = 98, statval = 99
注意,全局、静态和易失变量不受优化的影响,在调用longjmp后,它们的值是最近所呈现的值。
某个系统的setjmp(3)手册页上说明,存放在存储器中的变量将具有longjmp时的值,而在CPU和浮点寄存器中的变量则恢复为调用setjmp时的值。
不进行优化时,所有这5个变量都存放在存储器中(亦即忽略了对regival变量的register存储类说明)。而进行了优化后,autoval和regival都存放在寄存器中(即使autoval并未声明为register),volatile变量则仍存放在存储器中。
通过这一实例我们可以理解到,如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。
2、自动变量的潜在问题
基本规则是声明自动变量的函数已经返回后,不能再引用这些自动变量。
本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/。
进程环境之setjmp和longjmp函数的更多相关文章
- 【转载】setjmp和longjmp函数使用详解
[说明]本文上半部分转载自 wykwdy007 的转载文章 http://blog.csdn.net/wykwdy007/article/details/6535322 --------------- ...
- setjmp和longjmp函数使用详解
源地址:http://blog.csdn.net/zhuanshenweiliu/article/details/41961975 非局部跳转语句---setjmp和longjmp函数.非局部指的是, ...
- setjmp()和longjmp()函数
之前我们讲到了过程活动记录(AR),那么如何来操纵AR呢,一个可能的方法是,根据局部变量的地址进行推算,例如对于上面的a函数,执行a函数时的当前AR地址就是参数i的地址偏移8个字节,也就是 ((cha ...
- 学习之"setjmp和longjmp函数"
Linux学习之"setjmp和longjmp函数" 转贴,原文地址:http://www.cnblogs.com/lq0729/archive/2011/10/23/2222 ...
- linux系统编程:setjmp和longjmp函数用法
#include <stdio.h> #include <setjmp.h> //jmp_buf:数组,保存栈信息即运行环境 jmp_buf buf; double Divid ...
- setjmp和longjmp函数
关于setjmp函数和longjmp函数有话要说,是UNIX高级环境变成看到了10.10信号那章用到了,研究一下,这里作为补充. setjmp(jmp_buf env_buf) 函数可以将当前的运行环 ...
- 进程环境之getrlimit和setrlimit函数
每个进程都有一组资源限制,其中一些可以用getrlimit和setrlimit函数查询和更改. #include <sys/resource.h> int getrlimit( int r ...
- Linux-进程描述(5)之进程环境
main函数和启动例程 当内核使用一个exec函数执行C程序时,在调用main函数之前先调用一个特殊的启动例程,可执行程序将此例程指定为程序的起始地址.启动例程从内核获取命令行参数和环境变量,然后为调 ...
- apue学习笔记(第七章 进程环境)
本章将了解进程的环境. main函数 C程序总是从main函数开始执行,main函数的原型是: int main(int argc,char *argv[]); 其中,argc是命令行参数的数目,ar ...
随机推荐
- 解决“重新安装vmware-tools”灰色而无法安装的问题
前几天重装系统,之后虚拟机需要重新装,装好后要使用vmware-tools实现文件共享,却发现虚拟机那里显示为灰色的,无法安装vmware-tools,在共享文件夹那里设置好共享的文件夹猴也没有用,/ ...
- Linux 信号signal处理机制
信号是Linux编程中非常重要的部分,本文将详细介绍信号机制的基本概念.Linux对信号机制的大致实现方法.如何使用信号,以及有关信号的几个系统调用. 信号机制是进程之间相互传递消息的一种方法,信号全 ...
- 精通phthon的条件
1. 熟知主流硬件体系(x86, x64)2. 熟知 CPython 的具体实现,如若可能至少通读源码三遍以上3. 熟知每条 Python bytecode 如何被解释执行4. 熟知每条 Python ...
- tlplayer for android V2.7(支持变速不变调) 2014-07-20更新
2014-07-20新版本已经修复部分视频倾斜问题,已经更新到服务器,需要的朋友自行下载. 此版本修正了倾斜问题,增加水印等功能.可以动态添加水印. tlplayer for android V2.6 ...
- APP-PER-50022: Oracle Human Resources could not retrieve a value for the User Type profile option.
Symptoms ----------------------- AP > Setup > Organizations Show Error tips: APP-PER-50022: Or ...
- 常见 jar包详解
常见 jar包详解 jar包 用途 axis.jar SOAP引擎包 commons-discovery-0.2.jar 用来发现.查找和实现可插入式接口,提供一些一般类实例化.单件的生命周期 ...
- VJP1218数字游戏(环形DP)
链接 数据比较小 直接爆了 5重 枚举断开的琏 dp[i][j][k] (i-j)区间为第k段 dp[i][j][k] = min(dp[i][j][k],dp[g][i-1][k-1]*s[i][j ...
- java文件IO操作
package com.io; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream ...
- NOI2010海拔
2007: [Noi2010]海拔 Time Limit: 20 Sec Memory Limit: 552 MBSubmit: 1302 Solved: 612[Submit][Status] ...
- ☀【Zepto】
http://zeptojs.com/ https://github.com/madrobby/zepto Zepto 中文手册 http://www.360weboy.com/handbook/ze ...