操作系统编程设计:setjmp/longjmp

Introduction

在程序设计中,控制流(control flow)通常由Statement, Instruction set以及Subroutine组成。但有时候我们需要的逻辑无法由这些控制流方法得到,如try-catch-throw的异常机制。不过可喜的是,操作系统提供了额外的控制流的程序设计方法。

setjmp

setjmp的函数原型为:

int setjmp(jmp_buf env);

setjmp将当前的函数调用栈上下文以及环境信息保存在一个和操作系统相关的数据结构env中。
当 setjmp返回0时,表示函数在非longjmp的中返回;返回非0时,表示在longjmp中返回。

longjmp

longjmp的函数原型为:

void longjmp(jmp_buf env, int val);

longjmp将当前的函数调用栈上下文以及环境信息恢复为env中的环境。
由于longjmp不能return(否则函数调用栈会增加longjmp这一层),因此采用val作为返回值。
val的值即为setjmp的返回值,当val设置为0时,setjmp将视为返回值为1。

Example

下面给出一个使用setjmp/longjmp的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <setjmp.h>
static jmp_buf buffer;
void print_black() {
printf("I am black.\n");
longjmp(buffer, 1);
}
void print_white() {
print_black();
printf("I am white.\n");
}
int main() {
if (setjmp(buffer) == 0) {
print_white();
} else {
printf("I am gray.\n");
}
return 0;
}

编译运行程序:

$ gcc setjmpdemo.c -o setjmpdemo
$ ./setjmpdemo
I am black.
I am gray

发生了什么?

  1. 当第一次调用setjmp的时候,保存了当前程序的函数调用栈上下文以及环境信息。由于并非在longjmp中返回,因此setjmp返回值为0。
  2. if条件成立,执行print_white(),然后执行print_black()。
  3. 执行到print_black()中,首先打印”I am black.”,然后调用longjmp,恢复之前保存的函数调用栈上下文以及环境信息,并将setjmp返回值置为1。
  4. 由于函数调用栈上下文以及环境信息被恢复,此时程序还是处于setjmp处,但此时setjmp返回值为1。
  5. if条件不成立,进入else分支,打印”I am gray.”

注意在这个程序中,”I am white.”语句永远不会被执行。

Summary

通过简单的程序,介绍了setjmp/longjmp的控制流的程序设计方法。由于longjmp的返回值可以设置其他值,还可以通过对setjmp的返回值判断,实现不同的控制流。

(本文出自csprojectedu.com,转载请注明出处)