免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 13832 | 回复: 15

关于 atomic 的话题 [复制链接]

论坛徽章:
0
发表于 2009-11-20 21:14 |显示全部楼层
这里有一个很重要的概念:boundary (边界)

常见的 boundary:

★ word boundary(16 位)
★ doubleword boundary(32 位)
★ quadword boundary(64 位)

---------------------------------------------------------------------------------

1、造成这些 boundary 的原因是:data bus width (数据线的宽度)

  对于 processor 这里有一个很重要的分界点,以 Pentium 处理器为分界,在 Pentium 之前的 data bus 是 32 位,而 Pentiume 之后的 data bus 有 64 位。

  每 8 bits 分成一组 byte,由信号 BE# 来控制 read/write,被称为 data path

  所以,Pentiume 之后的 processor 有 8 个 data path(BE0 ~ BE7)

  就是由这些 data path 造成了 多种 boundary,这 8 个 data path 造成了可以有 quadword boundary 的出现

  但是 address bus 还是 32,以及后来的 48 位


2、 data path 是影响 boundary 和 atomic 的最主要的因素,

       而造成 非 atomic 的成因就是:跨 bounday,实际上是跨了 8 data path

   因此:跨了 8 个 data path 就会造成 非 atomic 的情况

   在 Pentium 之后的 processor 只要在不是跨 8 data path,即:quadword boudary 的情况下都能保证是 atomic


3、 何为跨 8 data path ?

BE0# ----> 0
BE1# ----> 1
BE2# ----> 2
BE3# ----> 3
BE4# ----> 4
BE5# ----> 5
BE6# ----> 6
BE7# ----> 7
-------------------------------------------
每个 data path 对应控制 1 个 byte

跨 quadword boudary 产生的情形:
  例如:当从 BE3#(3 data path) read/write 一个 quadword(64位),这时就属于跨 quadword boudary
  这时,processor 需要做 2 次 read/write 操作,这样,就不能保证 atomic

又例如:
  从 BE3# (3 data path) read/write  一个 word 操作,不会跨过 8 data path,它的 atomic 得到保证。



4、 另一些很坏的情况是:跨 cache line boundary,甚至是:跨 page boundary

  cache line boundary 是:大多数 processor 的 cache line 是 64 bytes(0 ~ 63 byte),如果一个 read/write 是跨过了 64 bytes 这个边界的话,若使用 lock 来保证它的 atomic 的话,processor 会先做 fill cache line 动作。

  page bundary 是:以 4K page 为例,同样若跨了 4K bytes 边界,processor 会做两次读 page




5、 以指令来说明“跨 boundary“

指令: mov eax, dword ptr [5]

  这条指令从 地址 5 开始读 doubleword 到 eax 寄存器

那么:
  (1) processor 先将 BE5# = 0,BE6# = 0,BE7# = 0,其它置 1,使用 data path 5、6、7 有效,其它 data path 无效,先读 24 位,放到 eax 的低 24 位

  (2) 接着 processor 再置 BE0# = 0,其它置1,使 data path 0 有效,其它 data path 无效,
   读 byte 放到 eax 的高 8 位

------------------------------------------------------------
  以这样来完成 doubleword 的读,这样 processor 实际上做了 2 次 read 工作。

  所以,atomic 将不能得到保证。

论坛徽章:
0
发表于 2009-11-20 21:21 |显示全部楼层
不能保证 atomic 的第二个因素是:processor 做 read-modify-write 类交易

典型的如:执行 add dword ptr [eax], eax 指令


对于这条指令:

1、processor 是 read 从内存 [eax] 一个 dword 放到 temp registers

2、processor 做 add 工作,temp registers 与 eax 相加结果放 temp registers

3、processor 写内存 [eax],从 temp registers

-------------------------------------------------------------

这样,就是 read-modify-write 操作不能保证 atomic


需要 lock 进行 atomic

论坛徽章:
1
天蝎座
日期:2013-10-23 21:11:03
发表于 2009-11-20 21:35 |显示全部楼层
问几个问题:
1) 编写一个程序,对齐是不是由编译器来操作?
2) Intel手册中没有明确说明的指令是不是都不能保证操作的原子性?印象中一条指令会分解成一些微操作序列(可能概念不太准
    确,具体忘了^^),是不是因为有的指令分解为比较复杂的操作序列而不能保证原子性?

谢谢!

论坛徽章:
0
发表于 2009-11-20 22:20 |显示全部楼层
原帖由 openspace 于 2009-11-20 21:35 发表
问几个问题:
1) 编写一个程序,对齐是不是由编译器来操作?
2) Intel手册中没有明确说明的指令是不是都不能保证操作的原子性?印象中一条指令会分解成一些微操作序列(可能概念不太准
    确,具体忘了^^) ...


1) 程序员配合编译器
2) 对内存进行 read-modify-write 操作的指令就不能保证 atomic

   如 add,sub,inc,dec,mul 等指令对内存进行 read-modify-write
 一些重要的系统指令,processor 会保证它的原子性

论坛徽章:
1
天蝎座
日期:2013-10-23 21:11:03
发表于 2009-11-20 23:26 |显示全部楼层

回复 #4 mik 的帖子

1)"程序员配合编译器"
     突然想起GCC里面的一些功能可以影响对齐,比如padding
     这样是不是可以理解如果仅仅是一般的编码,而不是用这些编译器提供的辅助功能的话数据可能并没有对齐?
     还是编译器会默认进行对齐处理,但是遇到特别指定的话再按照指示处理?因为经常讨论的sizeof看起来
     都是经过对齐处理了的
2)用另一个帖子里的例子
     int a = 1;
     int b = a;  // 这一句在32位机上是否能保证原子性
                     // 直觉上感觉需要    读a的值、获取b的地址、写入新值
                     
谢谢!

论坛徽章:
0
发表于 2009-11-20 23:41 |显示全部楼层
原帖由 openspace 于 2009-11-20 23:26 发表
2)用另一个帖子里的例子
     int a = 1;
     int b = a;  // 这一句在32位机上是否能保证原子性
                     // 直觉上感觉需要    读a的值、获取b的地址、写入新值 ...


是原子性

论坛徽章:
1
天蝎座
日期:2013-10-23 21:11:03
发表于 2009-11-21 00:08 |显示全部楼层

回复 #6 mik 的帖子

刚测试了3种情况

1

  1. int a = 1;

  2. int main(void)
  3. {
  4.         int b = a;
  5.         return 0;
  6. }
复制代码

  1.         .file        "test1_atomic.c"
  2. .globl a
  3.         .data
  4.         .align 4
  5.         .type        a, @object
  6.         .size        a, 4
  7. a:
  8.         .long        1
  9.         .text
  10. .globl main
  11.         .type        main, @function
  12. main:
  13.         pushl        %ebp
  14.         movl        %esp, %ebp
  15.         subl        $16, %esp

  16.         movl        a, %eax              // ***
  17.         movl        %eax, -4(%ebp)       // ***

  18.         movl        $0, %eax
  19.         leave
  20.         ret
  21.         .size        main, .-main
  22.         .ident        "GCC: (GNU) 4.4.2 20091027 (Red Hat 4.4.2-7)"
  23.         .section        .note.GNU-stack,"",@progbits
复制代码


2

  1. int a = 1;
  2. int b = 0;

  3. int main(void)
  4. {
  5.         b = a;
  6.         return 0;
  7. }
复制代码

  1.         .file        "test2_atomic.c"
  2. .globl a
  3.         .data
  4.         .align 4
  5.         .type        a, @object
  6.         .size        a, 4
  7. a:
  8.         .long        1
  9. .globl b
  10.         .bss
  11.         .align 4
  12.         .type        b, @object
  13.         .size        b, 4
  14. b:
  15.         .zero        4
  16.         .text
  17. .globl main
  18.         .type        main, @function
  19. main:
  20.         pushl        %ebp
  21.         movl        %esp, %ebp

  22.         movl        a, %eax        // ***
  23.         movl        %eax, b        // ***

  24.         movl        $0, %eax
  25.         popl        %ebp
  26.         ret
  27.         .size        main, .-main
  28.         .ident        "GCC: (GNU) 4.4.2 20091027 (Red Hat 4.4.2-7)"
  29.         .section        .note.GNU-stack,"",@progbits
复制代码


3


  1. int main(void)
  2. {
  3.         int a = 1;
  4.         int b = a;
  5.         return 0;
  6. }
复制代码

  1.         .file        "test3_atomic.c"
  2.         .text
  3. .globl main
  4.         .type        main, @function
  5. main:
  6.         pushl        %ebp
  7.         movl        %esp, %ebp
  8.         subl        $16, %esp
  9.         movl        $1, -8(%ebp)

  10.         movl        -8(%ebp), %eax    // ***
  11.         movl        %eax, -4(%ebp)    // ***

  12.         movl        $0, %eax
  13.         leave
  14.         ret
  15.         .size        main, .-main
  16.         .ident        "GCC: (GNU) 4.4.2 20091027 (Red Hat 4.4.2-7)"
  17.         .section        .note.GNU-stack,"",@progbits
复制代码


这样看b=a对应2条汇编语句,而且是通过mov操作完成的
感觉两条语句之间是有时间间隔的,这样CPU也能保证原子性?
而且Intel手册上说的也仅仅是满足一些条件的read or write操作可以保证原子性,这里涉及两个操作read and write,是否能保证原子性未知啊!

论坛徽章:
0
发表于 2009-11-21 00:22 |显示全部楼层

回复 #7 openspace 的帖子

int b = a;

代码的目的是令 b = a

(1)   movl        a, %eax                     // atomic
(2)   movl        %eax, -4(%ebp)       // atomic

-------------------------------------------------------------
虽然是2条指令,每条指令都是原子的。

(1) 中 a 已经 load 到 eax 中,即使读完后 a  被改变,不会影响 eax
(2) 中 eax 已经 store 到 -4(%ebp) 中,这个 eax 就是 a 值

>>> 即使 write 后,-4(%ebp) 被改写,那也是 write 完之后的事。

所以:int b = a;  这条语句是原子的,这样已经足够了

     即使之后 b 被改写,但 b =  a 语句来说,是原子的。

论坛徽章:
0
发表于 2009-11-21 00:27 |显示全部楼层
又例如:

b = a;      

----------------------------------------------

如果,编译器是这样编译的话:

movl    a,  -4(%ebp)                           //  a ---> temp
movl    -4(%ebp),  %eax                    //  temp ---> eax
movl   %eax, -8(%ebp)                      //  eax ---> b

--------------------------------------------------------------------

中间,多了一个 memory 读/写,这就不能保证其原子性了。

论坛徽章:
1
天蝎座
日期:2013-10-23 21:11:03
发表于 2009-11-21 09:33 |显示全部楼层

回复 #8 mik 的帖子

突然感觉跟版主理解的原子性不一样
一直考虑的是 事务原子性

明白了
谢谢版主了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP