gpt4 book ai didi

c - 有没有办法告诉 GCC 不要重新排序任何指令,而不仅仅是加载/存储?

转载 作者:太空狗 更新时间:2023-10-29 16:45:23 28 4
gpt4 key购买 nike

我正在研究 RTOS 的 irq_lock()/irq_unlock() 实现并发现了一个问题。我们希望绝对减少 CPU 锁定中断所花费的时间。现在,我们用于 x86 的 irq_lock() 内联函数使用“内存”破坏器:

static ALWAYS_INLINE unsigned int _do_irq_lock(void)
{
unsigned int key;

__asm__ volatile (
"pushfl;\n\t"
"cli;\n\t"
"popl %0;\n\t"
: "=g" (key)
:
: "memory"
);

return key;
}

问题在于,如果只接触寄存器而不接触内存,编译器仍会将可能昂贵的操作重新排序到临界区。一个具体的例子在我们内核的 sleep 函数中:

void k_sleep(int32_t duration)
{
__ASSERT(!_is_in_isr(), "");
__ASSERT(duration != K_FOREVER, "");

K_DEBUG("thread %p for %d ns\n", _current, duration);

/* wait of 0 ns is treated as a 'yield' */
if (duration == 0) {
k_yield();
return;
}

int32_t ticks = _TICK_ALIGN + _ms_to_ticks(duration);
int key = irq_lock();

_remove_thread_from_ready_q(_current);
_add_thread_timeout(_current, NULL, ticks);

_Swap(key);
}

执行昂贵数学运算的“滴答”计算在我们锁定中断的地方重新排序,因此我们调用 __divdi3 并锁定中断,这不是我们想要的:

Dump of assembler code for function k_sleep:
0x0010197a <+0>: push %ebp
0x0010197b <+1>: mov %esp,%ebp
0x0010197d <+3>: push %edi
0x0010197e <+4>: push %esi
0x0010197f <+5>: push %ebx
0x00101980 <+6>: mov 0x8(%ebp),%edi
0x00101983 <+9>: test %edi,%edi
0x00101985 <+11>: jne 0x101993 <k_sleep+25>
0x00101987 <+13>: lea -0xc(%ebp),%esp
0x0010198a <+16>: pop %ebx
0x0010198b <+17>: pop %esi
0x0010198c <+18>: pop %edi
0x0010198d <+19>: pop %ebp
0x0010198e <+20>: jmp 0x101944 <k_yield>
0x00101993 <+25>: pushf
0x00101994 <+26>: cli
0x00101995 <+27>: pop %esi
0x00101996 <+28>: pushl 0x104608
0x0010199c <+34>: call 0x101726 <_remove_thread_from_ready_q>
0x001019a1 <+39>: mov $0x64,%eax
0x001019a6 <+44>: imul %edi
0x001019a8 <+46>: mov 0x104608,%ebx
0x001019ae <+52>: add $0x3e7,%eax
0x001019b3 <+57>: adc $0x0,%edx
0x001019b6 <+60>: mov %ebx,0x20(%ebx)
0x001019b9 <+63>: movl $0x0,(%esp)
0x001019c0 <+70>: push $0x3e8
0x001019c5 <+75>: push %edx
0x001019c6 <+76>: push %eax
0x001019c7 <+77>: call 0x1000a0 <__divdi3>
0x001019cc <+82>: add $0x10,%esp
0x001019cf <+85>: inc %eax
0x001019d0 <+86>: mov %eax,0x28(%ebx)
0x001019d3 <+89>: movl $0x0,0x24(%ebx)
0x001019da <+96>: lea 0x18(%ebx),%edx
0x001019dd <+99>: mov $0x10460c,%eax
0x001019e2 <+104>: add $0x28,%ebx
0x001019e5 <+107>: mov $0x10162b,%ecx
0x001019ea <+112>: push %ebx
0x001019eb <+113>: call 0x101667 <sys_dlist_insert_at>
0x001019f0 <+118>: mov %esi,0x8(%ebp)
0x001019f3 <+121>: pop %eax
0x001019f4 <+122>: lea -0xc(%ebp),%esp
0x001019f7 <+125>: pop %ebx
0x001019f8 <+126>: pop %esi
0x001019f9 <+127>: pop %edi
0x001019fa <+128>: pop %ebp
0x001019fb <+129>: jmp 0x100f77 <_Swap>
End of assembler dump.

我们发现我们可以通过声明 'ticks' volatile 来获得我们想要的顺序:

Dump of assembler code for function k_sleep:
0x0010197a <+0>: push %ebp
0x0010197b <+1>: mov %esp,%ebp
0x0010197d <+3>: push %ebx
0x0010197e <+4>: push %edx
0x0010197f <+5>: mov 0x8(%ebp),%edx
0x00101982 <+8>: test %edx,%edx
0x00101984 <+10>: jne 0x10198d <k_sleep+19>
0x00101986 <+12>: call 0x101944 <k_yield>
0x0010198b <+17>: jmp 0x1019f5 <k_sleep+123>
0x0010198d <+19>: mov $0x64,%eax
0x00101992 <+24>: push $0x0
0x00101994 <+26>: imul %edx
0x00101996 <+28>: add $0x3e7,%eax
0x0010199b <+33>: push $0x3e8
0x001019a0 <+38>: adc $0x0,%edx
0x001019a3 <+41>: push %edx
0x001019a4 <+42>: push %eax
0x001019a5 <+43>: call 0x1000a0 <__divdi3>
0x001019aa <+48>: add $0x10,%esp
0x001019ad <+51>: inc %eax
0x001019ae <+52>: mov %eax,-0x8(%ebp)
0x001019b1 <+55>: pushf
0x001019b2 <+56>: cli
0x001019b3 <+57>: pop %ebx
0x001019b4 <+58>: pushl 0x1045e8
0x001019ba <+64>: call 0x101726 <_remove_thread_from_ready_q>
0x001019bf <+69>: mov 0x1045e8,%eax
0x001019c4 <+74>: mov -0x8(%ebp),%edx
0x001019c7 <+77>: movl $0x0,0x24(%eax)
0x001019ce <+84>: mov %edx,0x28(%eax)
0x001019d1 <+87>: mov %eax,0x20(%eax)
0x001019d4 <+90>: lea 0x18(%eax),%edx
0x001019d7 <+93>: add $0x28,%eax
0x001019da <+96>: mov %eax,(%esp)
0x001019dd <+99>: mov $0x10162b,%ecx
0x001019e2 <+104>: mov $0x1045ec,%eax
0x001019e7 <+109>: call 0x101667 <sys_dlist_insert_at>
0x001019ec <+114>: mov %ebx,(%esp)
0x001019ef <+117>: call 0x100f77 <_Swap>
0x001019f4 <+122>: pop %eax
0x001019f5 <+123>: mov -0x4(%ebp),%ebx
0x001019f8 <+126>: leave
0x001019f9 <+127>: ret
End of assembler dump.

然而,这只是将它修复在一个地方。我们确实需要一种方法来修改 irq_lock() 实现,以便它在任何地方都能做正确的事情,而现在“内存”破坏是不够的。

最佳答案

由于您的架构无论如何都是 x86,请尝试使用 __sync_synchronize 而不是内存破坏器。它是 x86 支持的完整硬件内存屏障。

关于c - 有没有办法告诉 GCC 不要重新排序任何指令,而不仅仅是加载/存储?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41044421/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com