我正在审查一些代码,并对使用的技术感到怀疑.
在Linux环境中,有两个进程连接多个共享内存段.第一个进程定期加载要共享的新文件集,并将共享内存id(shmid)写入"master"共享内存段中的某个位置.第二个过程不断读取此"主"位置,并使用shmid附加其他共享段.
在多CPU主机上,在我看来,如果一个进程在被另一个进程写入时尝试读取内存,可能会依赖于实现.但也许硬件级总线锁定可以防止线路上的损坏位?如果读取过程得到一个很快就要改变的值,那么只要读取被破坏为既不是旧值也不是新值的东西也是无关紧要的.这是一种边缘情况:只有32位被写入和读取.
谷歌搜索shmat的东西并没有让我在这方面做出任何明确的决定.
我强烈怀疑它不安全或理智,而我真正喜欢的是指向详细描述问题的文章的一些指示.
Jam*_*ese 12
它是合法的 - 因为在操作系统中不会阻止你这样做.
但它聪明吗?不,你应该有某种类型的同步.
电线上不会有"损坏的位".它们将以零或零的形式出现.但没有什么可说的,在另一个进程尝试读取它们之前,所有的位都会被写出来.并且无法保证它们的写入速度与读取速度有多快.
你应该总是假设2个进程(或者那个问题的线程)的动作之间绝对没有关系.
除非你做对,否则不会发生硬件级总线锁定.可能更难以使您的编译器/库/ os/cpu正确运行.编写同步原语以确保它正确发生.
锁定将使其安全,而且并不难.所以就这么做吧.
@unknown - 自从我的回答发布以来,问题已经有所改变.但是,您描述的行为依赖于平台(硬件,操作系统,库和编译器).
如果不给编译器特定的指令,实际上不能保证一次写入32位.想象一下32位字未在字边界上对齐的情况.这种未对齐的访问在x86上是可以接受的,并且在x68的情况下,访问被转换为cpu的一系列对齐访问.
这些操作之间可能发生中断.如果中间发生了上下文切换,则会写入一些位,有些则不会.砰,你死了.
另外,让我们考虑16位cpus或64位cpu.这两者仍然很受欢迎,并不一定按照你的想法运作.
所以,实际上你可能会遇到"其他一些cpu-core拾取字大小值1/2写入"的情况.如果不使用同步,就可以编写代码,好像预计会发生这种类型的事情.
现在,有一些方法可以预先形成你的写作,以确保你写出一个完整的单词.这些方法属于同步类别,创建同步原语是最好留给库,编译器,操作系统和硬件设计者的类型.特别是如果您对可移植性感兴趣(即使您从未移植过代码,也应该如此)
Rei*_*man 10
问题实际上比一些人讨论过的更糟糕.Zifre是正确的,在当前的x86 CPU上,内存写入是原子的,但这种情况很快就会停止 - 内存写入只是单个内核的原子写入 - 其他内核可能无法以相同的顺序看到写入.
换句话说,如果你这样做
a = 1;
b = 2;
Run Code Online (Sandbox Code Playgroud)
在CPU 2上,您可能会b在位置"a"之前看到位置已修改.此外,如果您写的值大于本机字大小(x32处理器上为32位),则写入不是原子的 - 因此64位写入的高32位将在与低位不同的时间点击总线32位写入.这会使事情变得非常复杂.
使用内存屏障,你会没事的.
你需要锁定某个地方.如果不是在代码级别,那么在硬件内存缓存和总线上.
你可能在后PentiumPro Intel CPU上没问题.从我刚刚读到的内容来看,英特尔后来的CPU基本上忽略了机器代码上的LOCK前缀.相反,缓存一致性协议确保所有CPU之间的数据一致.因此,如果代码写入的数据不会超过缓存行边界,那么它将起作用.不保证跨越缓存行的内存写入顺序,因此多字写入是有风险的.
如果您使用的不是x86或x86_64的任何其他那么你也不行.许多非Intel CPU(可能还有Intel Itanium)通过使用显式缓存一致性机器命令获得性能,如果您不使用它们(通过自定义ASM代码,编译器内部函数或库),则无法保证通过缓存写入内存永远变得对另一个CPU可见或以任何特定顺序发生.
因为某些东西在你的Core2系统上运行并不意味着你的代码是正确的.如果要检查可移植性,请在PPC(较旧的MacPro或Cell刀片)或Itanium或IBM Power或ARM等其他SMP体系结构上尝试您的代码.Alpha是一个很好的CPU,用于显示糟糕的SMP代码,但我怀疑你能找到一个.
| 归档时间: |
|
| 查看次数: |
4148 次 |
| 最近记录: |