diff --git a/02-setjmp/ReadMe.md b/02-setjmp/ReadMe.md new file mode 100644 index 0000000..4d10a4d --- /dev/null +++ b/02-setjmp/ReadMe.md @@ -0,0 +1,61 @@ + + +## 作用 +setjmp 和 longjmp 可以用来解决以下问题: +- 异常处理:在 C 中,缺乏类似 C++ 或其他语言的异常处理机制。setjmp 和 longjmp 提供了模拟异常处理的方式。 +- 非局部跳转:在复杂的函数调用栈中,可以使用它从深层函数直接跳回到某个点(通常是函数的顶部),以应对错误、资源释放等情况。 + +## 示例 + +RK3568(ARMv8)平台U-boot引导过程中 +```assembly +#---- u-boot/arch/arm/cpu/armv8/start.S +.globl _start +_start: + b reset + +reset: + /* Allow the board to save important registers */ + b save_boot_params // 弱符号,空实现。Rockchip默认实现(C语言,BROM已完成环境初始化),主要识别:进入下载模式或继续启动 +.globl save_boot_params_ret // 定义符号(函数) save_boot_params_ret +save_boot_params_ret: // 函数 save_boot_params_ret 执行位置 +... +master_cpu: + bl _main +``` +```c +#---- u-boot/arch/arm/mach-rockchip/bootrom.c +/* + * All Rockchip BROM implementations enter with a valid stack-pointer, + * so this can safely be implemented in C (providing a single + * implementation both for ARMv7 and AArch64). + */ +int save_boot_params(void) + int ret = setjmp(brom_ctx); // 设置跳点(并保存环境) + + switch (ret) { + case 0: // 下载模式 + if (check_back_to_brom_dnl_flag()) // 如果需要执行下载,寄存器中的BOOT_BROM_DOWNLOAD位 + _back_to_bootrom(BROM_BOOT_ENTER_DNL); + longjmp(brom_ctx, brom_cmd); // 重走重走switch,此时 ret = BROM_BOOT_ENTER_DNL, 即分支 BROM_BOOT_ENTER_DNL + save_boot_params_ret(); // 如果不需要下载,返回 Start.s 中 save_boot_params_ret 位置 + while (true) + /* does not return */; + break; + case BROM_BOOT_ENTER_DNL: + /* + * A non-zero return value will instruct the BROM enter + * download mode. + */ + ret = 1; + break; + ... +``` +说明: + 关键部分:非局部跳转的实现, setjmp 和 longjmp 实现了从特定函数(例如 save_boot_params)的不同执行路径之间的跳转。 + - 当进入下载模式或跳到下一个启动阶段时,通过 longjmp 可以使程序的控制流根据不同的条件进行相应的跳转。 + - setjmp 的返回值用于确定当前的跳转状态,并且程序根据该返回值进入不同的处理分支(例如下载模式、下一阶段启动等)。 + +局部跳转与非局部跳转的区别 +- 局部跳转:局部跳转通常发生在同一函数的不同位置之间,常见的例子是 goto 语句,它会跳转到同一个函数内部的其他位置,程序控制流的变化仅限于当前函数。 +- 非局部跳转:非局部跳转指的是跳转发生在多个函数的调用之间,即跳转目标可能在调用栈的更高层,甚至是跨越多个函数调用。这是 setjmp 和 longjmp 实现的核心功能。 diff --git a/02-setjmp/jmp.c b/02-setjmp/jmp.c index 6cd83b4..ab86954 100644 --- a/02-setjmp/jmp.c +++ b/02-setjmp/jmp.c @@ -22,5 +22,6 @@ int main() { // 从longjmp返回时,setjmp将返回非零值 printf("Back to main\n"); } + return 0; } \ No newline at end of file