V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
koc
V2EX  ›  问与答

哪位 V 友自信 C 语言掌握很好的… 这里有个问题

  •  
  •   koc · 2018-05-18 21:15:26 +08:00 · 2683 次点击
    这是一个创建于 2407 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近要用到 sigsetjmpsiglongjmp 函数,按照我的理解每次调用 sigsetjmp 后就会保存堆栈,调用 siglongjmp 就会恢复堆栈。但是谢了一个程序后发现和我的预期不符:

    https://gist.github.com/thekoc/f77a29da760f859bc4bc85f20f24de1b

    我认为在 testjmp 这个函数里,在打印 "jmp from main, y=%d\n" 时,y 应该等于 5,因为恢复了堆栈,但是实际上却是 0。

    实际的输出:

    didn't jmp from testjmp
    first run
    didn't jmp from main, y=5
    jump from testjmp
    jmp from main, y=0
    jump from testjmp
    jmp from main, y=0
    jump from testjmp
    jmp from main, y=0
    jump from testjmp
    jmp from main, y=0
    jump from testjmp
    

    请求解惑,感谢。

    15 条回复    2018-05-20 11:16:49 +08:00
    sfqtsh
        1
    sfqtsh  
       2018-05-18 21:33:02 +08:00 via Android   ❤️ 1
    你的理解是错的,这样结果就能说通了。
    搜一个 pc 指针。
    MiffyLiye
        2
    MiffyLiye  
       2018-05-18 21:42:37 +08:00   ❤️ 1
    这是在调操作系统 API,应该问对 UNIX/Linux/POSIX 掌握很好的人
    am241
        3
    am241  
       2018-05-18 22:02:11 +08:00   ❤️ 1
    印象里 setjmp 不会保存完整堆栈,是保存堆栈栈顶地址。调用者释放堆栈或者修改堆栈内容都可能会导致 longjmp 回来出问题
    koc
        4
    koc  
    OP
       2018-05-18 22:26:02 +08:00
    @MiffyLiye #2

    因为我看到 setjmp.h 是标准库就把它归类到 C 语言问题里了…

    @sfqtsh #1 @am241 #3
    谢谢回复,我找到了几个介绍原理的链接,再看看
    MiffyLiye
        5
    MiffyLiye  
       2018-05-18 23:12:24 +08:00
    @koc setjmp 确实是跨平台的,sigsetjmp 是 POSIX 特有的扩展
    koc
        6
    koc  
    OP
       2018-05-18 23:34:27 +08:00
    @MiffyLiye #5

    恩,我这个问题应该不是 sigsetjmp 相关的,因为根本没用到信号
    lcdtyph
        7
    lcdtyph  
       2018-05-19 00:18:52 +08:00 via iPhone
    @koc 原因是 setjmp 只当前栈帧的地址信息,不会复制整个栈帧。这导致了从 testjmp 跳回来的之后的那个 printf 调用把 testjmp 的堆栈覆盖了。你可以做个实验,在 testjmp 函数的开头声明一个大数组做缓冲,y 就不会被覆盖到了。
    比如
    int buffer[16];
    int y = 5;
    gnaggnoyil
        8
    gnaggnoyil  
       2018-05-19 09:28:13 +08:00
    LS 一堆误导人的.问题的症结就一句话:如果要想让 y 恢复原来的值,y 必须是 volatile 的.

    ISO/IEC 9899:2011 7.13.2.1 The longjmp function

    3.
    All accessible objects have values, and all other components of the abstract machine
    have state, as of the time the longjmp function was called, except that the values of
    objects of automatic storage duration that are local to the function containing the
    invocation of the corresponding setjmp macro that do not have volatile-qualified type
    and have been changed between the setjmp invocation and longjmp call are
    indeterminate.


    至于为什么这样规定也很好解释:说不定在某个实现里 automatic storage 的东西说不定就直接被编译器优化到寄存器里了呢.
    gnaggnoyil
        9
    gnaggnoyil  
       2018-05-19 09:40:03 +08:00
    http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsetjmp.html

    The ISO C standard specifies various restrictions on the usage of the setjmp() macro in order to permit implementors to recognize the name in the compiler and not implement an actual function. These same restrictions apply to the sigsetjmp() macro.

    POSIX 对于 setjmp/longjmp 的要求和 C 一样
    lcdtyph
        10
    lcdtyph  
       2018-05-19 10:05:48 +08:00 via iPhone
    @gnaggnoyil
    volatile 是用来防止在 setjmp 和 longjmp 之间对变量的修改被寄存器缓存掉。lz 的这个写法本来就没修改 y,而且不是标准写法,caller 和 callee 之间跳来跳去本来就不是正确的用法。
    y 没有恢复原来的值就是因为 main 里的后续调用覆盖了原本的 testjmp 堆栈。想知道谁对谁错很简单
    1. 查文档,做实验
    2. 反汇编
    而不是随便搜搜就发上来还要批判一番
    lance6716
        11
    lance6716  
       2018-05-19 10:38:14 +08:00 via Android
    我也要批判一下 v 站的程序员水平…刚刚 apue 学完 setjmp,要是想 y=5 的话需要成功申请为 register 类型。
    lance6716
        12
    lance6716  
       2018-05-19 10:49:28 +08:00 via Android
    不好意思,自己试了试 register 是会变的…被老师坑了
    gnaggnoyil
        13
    gnaggnoyil  
       2018-05-19 11:08:41 +08:00
    @lcdtyph 我的错……确实 LZ 的问题和 y 是不是 volatile 的没关系……就在我原来引文的同一个地方的第二小段就有这句……"If ...the function containing the invocation of the setjmp macro has terminated execution in the interim, ...... the behavior is undefined."
    gnaggnoyil
        14
    gnaggnoyil  
       2018-05-19 11:10:41 +08:00
    @lcdtyph "The longjmp function restores the environment saved by the most recent invocation of the setjmp macro in the same invocation of the program with the corresponding jmp_buf argument. If ...the function containing the invocation of the setjmp macro has terminated execution in the interim, ...... the behavior is undefined."
    koc
        15
    koc  
    OP
       2018-05-20 11:16:49 +08:00 via iPad
    谢谢各位的回复,我大概知道发生什么了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1018 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 21:15 · PVG 05:15 · LAX 13:15 · JFK 16:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.