C语言的 setjmp 和 longjmp

301 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

最近在做南京大学蒋炎岩老师的操作系统课程,实验M2是实现一个协程库,其中提到了 setjmp 和 longjmp ,这个之前在学习c 语言的时候还没有注意到过,所以就在这里总结一下他们的作用和用法。

在 C 语言中,我们不能使用 goto 语句来跳转到另一个函数中的某个 label 处;但提供了两个函数——setjmp 和 longjmp来完成这种类型的分支跳转。后面我们会看到这两个函数在处理异常上面的非常有用。

setjmp 和 longjmp 使用方法

我们都知道要想在一个函数内进行跳转,可以使用 goto 语句(在很多教科书上其实是并不推荐 使用 goto 语句的,因为goto语句的 存在会影响程序按照顺序执行的过程,给人一种跳来跳去的感觉,比较影响程序的可读性。)

但是如果遇到一下场景,可以很方便的为我们提供便利。


label

for (int i; i<n; i++) {
    for (int j; j<n; j++) {
        if {
            ·····
        }else{
            // goto label
        }
    }
}

goto 语句可以很方便的让我们跳出多成堆叠的层次。(但这些内容仅仅限于单一个程序内部)

但如果从一个函数内跳转到另一个函数的某处,goto 是不能完成的,那该如何实现呢?

  • 函数栈帧,主要是栈帧指针BP和栈顶指针SP

  • 程序指针PC,此处为指向 Label 语句的地址

  • 其它寄存器,这是和体系相关的,在 x86 体系下需要保存有的 AX/BX/CX 等等 callee-regs。

  • 参数 env 是由 setjmp 函数保存过的上下文。

  • 参数 val 表示从 longjmp 函数传递给 setjmp 函数的返回值,如果 val 值为0, setjmp 将会返回1,否则返回 val。

  • longjmp 不直接返回,而是从 setjmp 函数中返回,longjmp 执行完之后,程序就像刚从 setjmp 函数返回一样。

image.png

i = 0
i = 2

这个操作十分有用,他可以让我们实现一些神奇的操作 比如说 在c 语言里边儿 实现 python 里边儿 的yield 操作。

image.png

image.png