Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么此代码与单个printf的行为不同?ucontext.h 当我编译下面的代码时,它会打印出来_C_Compiler Optimization_Context Switching - Fatal编程技术网

为什么此代码与单个printf的行为不同?ucontext.h 当我编译下面的代码时,它会打印出来

为什么此代码与单个printf的行为不同?ucontext.h 当我编译下面的代码时,它会打印出来,c,compiler-optimization,context-switching,C,Compiler Optimization,Context Switching,我正在跑步:) 永远(直到我向程序发送键盘中断信号), 但是当我取消注释//printf(“完成:%d\n”,完成)时,重新编译并运行它,它将只打印两次,打印done:1,然后返回。 我是ucontext.h的新手,我对这段代码的工作原理和 如果将printf替换为done++,为什么单个printf会改变代码的整个行为它也会这样做,但如果您将其替换为done=2它不会影响任何东西,并且可以正常工作,因为我们首先对printf进行了注释。 谁能解释一下: 为什么这段代码是这样的,其背后的逻辑是什

我正在跑步:)

永远(直到我向程序发送键盘中断信号),
但是当我取消注释
//printf(“完成:%d\n”,完成)时,重新编译并运行它,它将只打印两次,打印
done:1
,然后返回。
我是ucontext.h的新手,我对这段代码的工作原理和 如果将
printf
替换为
done++,为什么单个printf会改变代码的整个行为
它也会这样做,但如果您将其替换为
done=2它不会影响任何东西,并且可以正常工作,因为我们首先对
printf
进行了注释。
谁能解释一下:
为什么这段代码是这样的,其背后的逻辑是什么?
对不起,我的英语不好,
非常感谢

#include <ucontext.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>


int main()
{
    register int done = 0;
    ucontext_t one;
    ucontext_t two;
    getcontext(&one);
    printf("I am running :)\n");
    sleep(1);
    if (!done)
    {
        done = 1;  
        swapcontext(&two, &one);
    }
    // printf("done:%d\n", done);
    return 0;
}
#包括
#包括
#包括
#包括
int main()
{
寄存器int done=0;
ucontext_t one;
u上下文二;
getcontext(&one);
printf(“我正在运行:)\n”);
睡眠(1);
如果(!完成)
{
完成=1;
swapcontext(两个和一个);
}
//printf(“完成:%d\n”,完成);
返回0;
}

这是一个编译器优化“问题”。注释“printf()”时,编译器推断“if(!done)”之后不会使用“done”,因此不会将其设置为1,因为它不值得。但是,当“printf()”存在时,在“if(!done)”之后使用“done”,因此编译器会设置它

带有“printf()”的汇编代码:

$gcc ctx.c-o ctx-g
$objdump-S ctx
[...]
内部主(空)
{
11e9:f3 0f 1e fa endbr64
11ed:55%按需付费
11ee:48 89 e5 mov%rsp,%rbp
11f1:48 81欧共体b0 07 00分$0x7b0,%rsp
11f8:64 48 8b 04 25 28 00 mov%fs:0x28,%rax
11ff:00
1201:48 89 45 f8移动百分比rax,-0x8(%rbp)
1205:31 c0异或%eax,%eax
寄存器int done=0;
1207:c7 85 5c f8 ff 00 movl$0x0,-0x7a4(%rbp)(在我写这篇文章时,与Rachid K的答案有些重叠。)

我猜您将
done
声明为
register
,希望它实际上会被放入寄存器中,以便通过上下文切换保存和恢复其值。但是编译器从来没有义务遵守这一点;大多数现代编译器完全忽略
register
声明,并自行决定特别是,没有优化的
gcc
几乎总是将局部变量放在堆栈的内存中

同样,在您的测试用例中,
done
的值不会被上下文开关还原。因此,当
getcontext
第二次返回时,
done
的值与调用
swapcontext
时的值相同

当存在
printf
时,正如Rachid指出的那样,
done=1
实际上存储在
swapcontext
之前,因此在第二次返回
getcontext
时,
done
的值为1,跳过
if
块,程序打印
done:1
并退出

但是,当
printf
不存在时,编译器会注意到
done
的值在赋值后从未使用过(因为它假设
swapcontext
是一个普通函数,并且不知道它实际上会在其他地方返回),因此它会优化出(是的,即使优化已关闭)。因此,当
getcontext
第二次返回时,我们得到了
done==0
,并且您得到了一个无限循环。如果您认为
done
将被放入寄存器中,这可能是您所期望的,但是如果是这样,您得到的“正确”行为的原因是错误的

如果启用优化,您将再次看到其他内容:编译器注意到,
done
不会受到对
getcontext
的调用的影响(再次假设这是一个正常的函数调用)因此,它在
if
时保证为0。因此根本不需要进行测试,因为它始终为真。
swapcontext
随后无条件执行,而对于
done
,它完全不存在,因为它不再对代码有任何影响。您将再次看到无穷大e环

由于此问题,您确实无法对在
getcontext
swapcontext
之间修改的局部变量做出任何安全的假设。当
getcontext
第二次返回时,您可能会看到更改,也可能看不到更改。如果编译器选择对其中的一些变量重新排序,则会出现进一步的问题您的代码围绕函数调用(它不知道为什么不这样做,因为它再次认为这些是普通函数调用,看不到您的局部变量)


获得任何确定性的唯一方法是声明一个变量
volatile
。然后您可以确保看到中间更改,并且编译器不会假定
getcontext
无法更改它。在
getcontext
的第二次返回时看到的值将与调用
swapcontext
时看到的值相同。如果您编写
volatile int done=0;
您应该只看到两个“我正在运行”消息,无论其他代码或优化设置如何。

您使用的编译器和选项是什么?@NateEldredge gcc,使用
gcc FILE_NAME.c-o FILE_NAME.o
尝试
gcc-o-Wall-Wextra-g FILE_NAME.c-o progname
我认为这是不对的,如果您使用
done=2;
更改printf,它不会中断循环,也不会停止oes forever,而且我们正在使用if块中的
swapcontext(&two,&one);
更改上下文,并且它不应该脱离if块(根据我从