|
定义:
#define barrier() __asm__ __volatile__("": : :"memory")
说明:
__volatile__ 告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。
在内嵌汇编中,指令部分为空,输出位置为空,输入位置为空,但它用 memory 强制gcc编译器假设 RAM 所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令,而避免去访问内存。。这个用法就是防止内存屏障。
内核的 kernel/sched.c 中的 context_switch() 函数里有一处用法:
* Here we just switch the register state and the stack. */ | switch_to(prev, next, prev); | | barrier(); | /* | * this_rq must be evaluated again because prev may have moved | * CPUs since it called schedule(), thus the 'rq' on its stack | * frame will be invalid. | */ | finish_task_switch(this_rq(), prev); |
context_switch() 函数负责从当前进程切换到另一个进程环境,但当前进程需要修改某些内核数据结构,而这些修改也需要反映到另外一个进程中,因此这里插入 barrier() ,以保证内存变量和对应的寄存器的一致。
switch_to 代码: #define switch_to(prev,next,last) do { \
unsigned long esi,edi; \
asm volatile("pushfl\n\t" /* Save flags */ \
"pushl %%ebp\n\t" \
"movl %%esp,%0\n\t" /* save ESP */ \
"movl %5,%%esp\n\t" /* restore ESP */ \
"movl $1f,%1\n\t" /* save EIP */ \
"pushl %6\n\t" /* restore EIP */ \
"jmp __switch_to\n" \
"1:\t" \
"popl %%ebp\n\t" \
"popfl" \
:"=m" (prev->thread.esp),"=m" (prev->thread.eip), \
"=a" (last),"=S" (esi),"=D" (edi) \
:"m" (next->thread.esp),"m" (next->thread.eip), \
"2" (prev), "d" (next)); \
} while (0)
补充:
CPU越过内存屏障后,将刷新自己对存储器的缓冲状态。这条语句(barrier())实际上不生成任何代码,但可使gcc在 barrier()之后刷新寄存器对变量的分配。 也就是说,barrier()宏只约束gcc编译器,不约束运行时的CPU行为。 |
|