erReader.write WriterReader
25. 1 0x2000000001de81c0: adds r37=592,r36;; ;...0b284149 0421
26. 2 0x2000000001de81c6: st4.rel [r37]=r39 ;...00389560 2380
27. 3 0x2000000001de81cc: adds r36=596,r36;; ;...84112544
28. 4 0x2000000001de81d0: st1.rel [r36]=r0 ;...09000048 a011
29. 5 0x2000000001de81d6: mf ;...00000044 0000
30. 6 0x2000000001de81dc: nop.i 0x0;; ;...00040000
31. 7 0x2000000001de81e0: mov r12=r33 ;...00600042 0021
32. 8 0x2000000001de81e6: mov.ret b0=r35,0x2000000001de81e0
33. 9 0x2000000001de81ec: mov.i ar.pfs=r34 ;...00aa0220
34.10 0x2000000001de81f0: mov r6=r32 ;...09300040 0021
这里我们可以看到在第四行第二次写操作被注解了一个显式内存屏障。通过使用st.rel,即“存储释放”(store release),编译器确保第一次写操作在第二次写操作之前完成。这就完成了两边的约定,因为第一次写操作在第二次写操作之前发生。
st.rel屏障是单向的--就像ld.acq一样。但是在第五行编译器设置了一个双向内存屏障。mf指令,或者称为“内存栅栏”,是Itanium 2指令集中的完整栅栏。笔者认为是多余的。
内存屏障是特定于硬件的
本文不想针对所有内存屏障做一综述。这将是一件不朽的功绩。但是,重要的是认识到这些指令在不同的硬件体系中迥异。下面的指令是连续写操作在多处理 Intel Xeon硬件上编译的结果。本文后面的所有汇编指令除非特殊声明否则都出自于Intel Xeon。
1.1 0x03f8340c: push %ebp ;...55
2. 2 0x03f8340d: sub $0x8,%esp ;...81ec0800 0000
3. 3 0x03f83413: mov $0x14c,%edi ;...bf4c0100 00
4. 4 0x03f83418: movb $0x1,-0x505a72f0(%edi) ;...c687108d a5af01
5. 5 0x03f8341f: mfence ;...0faef0
6. 6 0x03f83422: mov $0x148,%ebp ;...bd480100 00
7. 7 0x03f83427: mov $0x14d,%edx ;...ba4d0100 00
8. 8 0x03f8342c: movsbl -0x505a72f0(%edx),%ebx ;...0fbe9a10 8da5af
9. 9 0x03f83433: test %ebx,%ebx ;...85db
10.10 0x03f83435: jne 0x03f83460 ;...7529
11.11 0x03f83437: movl $0x1,-0x505a72f0(%ebp) ;...c785108d a5af01
12.12 0x03f83441: movb $0x0,-0x505a72f0(%edi) ;...c687108d a5af00
13.13 0x03f83448: mfence ;...0faef0
14.14 0x03f8344b: add $0x8,%esp ;...83c408
15.15 0x03f8344e: pop %ebp ;...5d