免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 17116 | 回复: 13
打印 上一主题 下一主题

修改指针值的操作是一个原子操作? [复制链接]

论坛徽章:
0
1 [报告]
发表于 2008-05-18 15:22 |显示全部楼层
原帖由 iterator 于 2008-5-18 14:54 发表
ULK上在讲rcu的时候,提到写者会修改指针,然后说"修改指针值的操作是一个原子操作".
为什么是原子操作呢??怎么保证的?

在x86上单一的写是原子的。每个写操作之前会发起cache同步,所以一个CPU在写的时候,其它CPU无法发起cache同步,必须等该CPU写完(具体机制我也不清楚,原理应该是这样的)。
如果修改指针仅仅是给指针赋值,那就是一个单一的写操作,是原子的。

论坛徽章:
0
2 [报告]
发表于 2008-05-19 11:40 |显示全部楼层
原帖由 iterator 于 2008-5-19 10:48 发表
多谢.可3楼上怎么又说必须是32位对齐的呢?

另外,请教cacheable memory和externel memory subsystems都是什么意思?

有不对齐的指针吗?
crpso贴的是IA32 spec关于原子操作的规定,里面内容有些是晦涩和不清楚的,例如“16-bit accesses to uncached memory locations that fit within a 32-bit data bus”
通常我们把它归纳成:对于单一的读写操作,对齐的访问是原子的,cacheline内的访问是原子的。
cacheable memory,大部分内存都是cacheable的,这里的cache指用CPU的cache。
externel memory subsystems,应该是指除了本地内存控制器直接访问的内存外,还有通过其它总线间接访问的内存。我觉得NUMA应该属于这种情况。

[ 本帖最后由 zx_wing 于 2008-5-19 11:42 编辑 ]

论坛徽章:
0
3 [报告]
发表于 2008-05-19 12:41 |显示全部楼层
原帖由 iterator 于 2008-5-19 12:31 发表
还是不太懂.要是一个结构体用attribute packed声明了,它内部的指针成员不就有可能不对齐了吗?

不行的,__attribute__ ((packed))不是万能的,它无法构造一个不对齐的指针。
例如
struct
{
   char a;
   char *str;
   char b;
} __attribute__ ((packed)) x;

仍然会对齐

论坛徽章:
0
4 [报告]
发表于 2008-05-19 12:46 |显示全部楼层
`packed'
     The `packed' attribute specifies that a variable or structure field
     should have the smallest possible alignment--one byte for a
     variable,
and one bit for a field, unless you specify a larger
     value with the `aligned' attribute.

packed带来的紧凑模式是以最小单位对齐,并不是不对齐。

论坛徽章:
0
5 [报告]
发表于 2008-05-19 13:17 |显示全部楼层
原帖由 iterator 于 2008-5-19 12:48 发表


那个str应该不是32位对齐的呀

对,我刚才想叉了,它没对齐到自然边界,按理是有问题的。
但我在RSIC平台上做了一个实验,在这个平台上,不对齐的访问会引发一个错误。非常意外的发现,结构体内的不对齐的指针赋值没有问题,但自己构造的未对齐指针赋值就有错。下面是程序和结果
1 #include <stdio.h>
  2 #include <stdlib.h>
  3
  4 struct p
  5 {
  6     char a;
7     char *str;               //这里的指针也没对齐到自然边界
  8     char b;
  9 } __attribute__ ((packed)) x;
10
11 int main()
12 {
13     char buf[10];
14     unsigned long addr = (unsigned long)&x.str;
15     int *pp = (int *)&buf[1];                 //这是个不对齐的数据访问
16     char **str = (char **)&buf[1];        //这个是个不对齐的指针

17
18     *pp = 100;                 //出错
19     *str = "hello";            //出错

20     printf("%d,%#lx\n", sizeof(x), addr);
21     x.str = "hello";           //没问题
22     printf("%s\n", x.str);
23
24 }


下面是运行结果:
[xin@vti-build tmp]$ ./t_align
t_align(18426): unaligned access to 0x60000fffffffb6a1, ip=0x4000000000000690
t_align(18426): unaligned access to 0x60000fffffffb6a1, ip=0x40000000000006b1

10,0x60000fffffffb691
hello

注意3个地址都是奇数结尾的,都没对齐到自然边界,但结构体内那个操作没出错


糊涂了,大家有什么看法,我也再想想

论坛徽章:
0
6 [报告]
发表于 2008-05-19 14:21 |显示全部楼层
原帖由 zx_wing 于 2008-5-19 13:17 发表

对,我刚才想叉了,它没对齐到自然边界,按理是有问题的。
但我在RSIC平台上做了一个实验,在这个平台上,不对齐的访问会引发一个错误。非常意外的发现,结构体内的不对齐的指针赋值没有问题,但自己构造的未 ...

对这个问题我研究了一下,通过反汇编,看出了问题
实际上x.str = "hello"; 这句,编译器保证了对齐访问。
策略可以用下面伪代码表示(我的机器是64bit的):
unsigned long addr = (unsigned long)"hello";
unsigned long first_write =  (addr >> 8) | (x.a << 56);
unsigned long sec_write = (addr << 8) | (x.b);

*(unsigned long *)&x = first_write;
*(unsigned long *)((void *)&x + 8) = sec_write;

可见,编译器用两次对齐到64bit边界的写操作完成了对未对齐到64bit边界的指针的赋值。
从中我们或许可以得到两个结论:
1、在对齐要求严格的平台上,编译器可以感知结构体内非对齐的访问,将它转换成对齐操作。
2、在x86平台上,未对齐到自然边界的指针赋值,应该是非原子的。

大家是对的哈,我前面的两个回复错了。

[ 本帖最后由 zx_wing 于 2008-5-19 15:11 编辑 ]

论坛徽章:
0
7 [报告]
发表于 2008-05-19 14:35 |显示全部楼层
原帖由 gvim 于 2008-5-19 14:24 发表
>>>但我在RSIC平台上做了一个实验
与risc无关,依赖具体实现和编译器。

有关系的,CISC平台,通常是x86,不会引起未对齐错误,因为是硬件自己处理
RISC平台,会产生一个未对齐的异常。通常会报告给出错的程序。
当然,我不知道是不是所有RISC平台都如此。

出错的情况我已经贴出来了。
版主如果在你的平台上发现不同,希望给出你的平台和编译器的行为
我已经解释了在IA64平台上,编译器如何处理。

论坛徽章:
0
8 [报告]
发表于 2008-05-19 14:36 |显示全部楼层
原帖由 gvim 于 2008-5-19 14:27 发表
*str = "hello";            //出错

???

对,出错
t_align(18426): unaligned access to 0x60000fffffffb6a1, ip=0x4000000000000690
t_align(18426): unaligned access to 0x60000fffffffb6a1, ip=0x40000000000006b1
对应程序中的:
18     *pp = 100;                 //出错
19     *str = "hello";            //出错

论坛徽章:
0
9 [报告]
发表于 2008-05-19 16:37 |显示全部楼层
原帖由 gvim 于 2008-5-19 15:44 发表
>>>RISC平台,会产生一个未对齐的异常。通常会报告给出错的程序。
arm926ej-s
并不是所有的RISC都会抱你说的“未对齐”错误。


>>>*str = "hello";
你可以把它注释掉看看有没有错误。*str引向一个unkno ...

好的,看来不是所有RISC平台都要求严格对齐的。
注释掉当然没有错误。
*str指向的是buf[1]开始的8个字节内存区域。
给它赋值也就是写buf[1]开始的8个字节,该区域存在于栈上,我们写它完全是合法应用。
不是版主说的unkonw的区域哈,如果是那样,系统会产生segfault,而非未对齐错误。

**str="hello"才是版主说的引用unkonwn区域,而引起segfault。

[ 本帖最后由 zx_wing 于 2008-5-19 16:49 编辑 ]

论坛徽章:
0
10 [报告]
发表于 2008-05-19 16:42 |显示全部楼层
原帖由 gvim 于 2008-5-19 15:44 发表
>>>RISC平台,会产生一个未对齐的异常。通常会报告给出错的程序。
arm926ej-s
并不是所有的RISC都会抱你说的“未对齐”错误。


>>>*str = "hello";
你可以把它注释掉看看有没有错误。*str引向一个unkno ...

好奇
arm926ej-s平台如何解决未对齐?
我听说arm也是要严格对齐的啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP