简体   繁体   English

为什么setjmp / longjmp在MinGW中没有时会在MSVC上崩溃?

[英]Why does the setjmp/longjmp crash on MSVC when it didn't in MinGW?

I wanna use setjmp() / longjmp() to implement a coroutine system. 我想使用setjmp() / longjmp()来实现一个协程系统。 Then I decide to code a little .c file to test it. 然后我决定编写一个.c文件来测试它。 In MinGW, it's OK; 在MinGW,没关系; I got the result I want. 我得到了我想要的结果。 But when I compile it in MSVC++, the program crashes: "access violation" 但是当我在MSVC ++中编译它时,程序崩溃:“访问冲突”

    #include <stdio.h>
    #include <stdlib.h>
    #include <setjmp.h>

    jmp_buf a;
    int is_invoke=0;

    void 
    action_1()
    {
        for ( ;; ) {
          printf("hello~~~A\n");
          if(!setjmp(a)) {
            is_invoke=1;
            return;
          }  
        }
    }

    void 
    func()
    {
      if (is_invoke) {
        longjmp(a,1);
      }
      action_1();
      printf("end\n");
    }

    void 
    dummy()
    {
      ;
    }

    int 
    main(int argc, char *argv[])
    {
        for ( ;; )  {
          func();
          dummy();
        }
        return 0;
    }

The man page for setjmp says: setjmp的手册页说:

setjmp() saves the stack context/environment in env for later use by longjmp() . setjmp()将堆栈上下文/环境保存在env供以后使用longjmp() The stack context will be invalidated if the function which called setjmp() returns. 如果调用setjmp()的函数返回,则堆栈上下文将无效。

In a simple implementation you might suppose that a jmp_buf contains an address to reset the stack pointer to and an address to jump to. 在一个简单的实现中,你可能会认为jmp_buf包含一个地址来重置堆栈指针和一个跳转到的地址。 As soon as you return from the function which saved the jmp_buf , the stack frame pointed to by the jmp_buf is no longer valid and may immediately become corrupted. 只要你从保存在函数返回jmp_buf ,栈帧指向的jmp_buf不再有效,并且可以立即被破坏。

Or in other words, you can only rely on longjmp to act as a sort-of super- return statement - never to go deeper. 换句话说,你只能依靠longjmp作为一种超级return声明 - 永远不要深入。

I think the reason this works for you in mingw (and for me on Linux) is implementation-specific and possibly down to luck. 我认为这对你来说在mingw(以及我在Linux上)的作用的原因是特定于实现的,可能还有运气。 There is another way - have you read Simon Tatham's evil coroutine macros essay? 还有另外一种方法 - 你读过西蒙塔特姆的邪恶的协程宏文章吗?

Since you're invoking undefined behaviour, it's OK for one compiler to crash and another to appear to work. 由于您正在调用未定义的行为,因此一个编译器崩溃并且另一个编译器似乎可以正常工作。 Both are correct - that's the beauty of undefined behaviour. 两者都是正确的 - 这是未定义行为的美妙。

The trouble is that a saved context - the jmp_buf - only remains valid as long as the function that called setjmp() to set it has not returned. 问题是保存的上下文 - jmp_buf - 只有在调用setjmp()来设置它的函数没有返回时才会保持有效。

The C99 standard (no longer the current standard, but this wording is unlikely to have changed significantly) says: C99标准(不再是现行标准,但这种措辞不太可能发生重大变化)说:

§7.13.2.1 The longjmp function §7.13.2.1的longjmp功能

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. longjmp函数使用相应的jmp_buf参数恢复最近调用setjmp宏所保存的环境。 If there has been no such invocation, or if the function containing the invocation of the setjmp macro has terminated execution 208) in the interim, or if the invocation of the setjmp macro was within the scope of an identifier with variably modified type and execution has left that scope in the interim, the behavior is undefined. 如果没有这样的调用,或者包含setjmp宏的调用的函数在过渡期间终止执行208) ,或者如果setjmp宏的调用在具有可变修改类型的标识符的范围内并且执行具有在过渡期间将该范围留下,行为未定义。

208) For example, by executing a return statement or because another longjmp call has caused a transfer to a setjmp invocation in a function earlier in the set of nested calls. 208)例如,通过执行return语句或因为另一个longjmp调用导致在嵌套调用集合中较早的函数中转移到setjmp调用。

Your code is exiting from action_1() almost immediately, rendering the jmp_buf saved by setjmp() worthless. 您的代码几乎立即退出action_1() ,使得setjmp()保存的jmp_buf毫无价值。


I created this little demonstration of setjmp() and longjmp() a couple of years ago. 几年前我创建了setjmp()longjmp()的这个小演示。 It may help you. 它可能会帮助你。

/*
@(#)File:           $RCSfile: setjmp.c,v $
@(#)Version:        $Revision: 1.1 $
@(#)Last changed:   $Date: 2009/10/01 16:41:04 $
@(#)Purpose:        Demonstrate setjmp() and longjmp()
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 2009
*/


#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>

static jmp_buf target_location;

static void do_something(void)
{
    static int counter = 0;
    if (++counter % 10 == 0)
        printf("---- doing something: %3d\n", counter);
    if (counter % 1000 == 0)
    {
        printf("||-- doing_something: calling longjmp() with value -1\n");
        longjmp(target_location, -1);
    }
}

static void do_something_else(int i, int j)
{
    printf("-->> do_something_else: (%d,%d)\n", i, j);
    do_something();
    if (i > 2 && j > 2 && j % i == 2)
    {
        printf("||-- do_something_else: calling longjmp() with value %d\n", (i + j) % 100);
        longjmp(target_location, (i + j) % 100);
    }
    printf("<<-- do_something_else: (%d,%d)\n", i, j);
}

static void doing_stuff(void)
{
    int i;
    printf("-->> doing_stuff()\n");
    for (i = rand() % 15; i < 30; i++)
    {
        int j;
        do_something();
        for (j = rand() % 10; j < 20; j++)
        {
            do_something_else(i, j);
        }
    }
    printf("<<-- doing_stuff()\n");
}

static void manage_setjmp(void)
{
    printf("-->> manage_setjmp()\n");
    switch (setjmp(target_location))
    {
    case 0:
        /* Initial return - get on with doing stuff */
        doing_stuff();
        break;
    case -1:
        /* Error return - terminate */
        printf("<<-- manage_setjmp() - error return from setjmp()\n");
        return;
    default:
        /* NB: not officially possible to assign the return from setjmp() */
        printf("---- manage_setjmp() - non-error return from setjmp()\n");
        doing_stuff();
        break;
    }
    printf("<<-- manage_setjmp()\n");
}

int main(void)
{
    printf("-->> main()\n");
    manage_setjmp();
    printf("<<-- main()\n");
    return(0);
}

You cannot use setjmp/longjmp for coroutines. 您不能将setjmp / longjmp用于协同程序。 Use makecontext/swapcontext on POSIX, or fibers (CreateFiber, etc.) on windows. 在POSIX上使用makecontext / swapcontext,或在Windows上使用光纤(CreateFiber等)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM