是否有可能存储指针在共享存储器没有使用失调?
-
21-09-2019 - |
题
当使用共享存储器,每种过程可能mmap共享的地区进入不同地区的其各自的地址空间。这意味着,当储存的指针在内的共用区,你需要 它们存储为抵消 开始的共同区域。不幸的是,这种复杂利用原子的指令(例如如果你想要写 锁免费的算法).例如说你有一大堆的参考算中的节点分享内存,创建一个单一的作家。作者定期自动更新的指针'p'指一种有效的节点积极参数。读者想要原子写信给'p',因为它指的开始的一个节点(一个结构),其第一个元素是一个基准数。由于p总是点到有效的节点,增加ref计是安全的,并使其安全引用'p'和访问的其他成员。然而,这只能当一切都是在同一地址的空间。如果节点和'p'指的是存储在共享存储器,那么客户受到比赛的条件:
- x=读p
- y=x+偏
- 增量引用次数在y
在步骤2,p可以改变和x可能不再点一个有效的节点。唯一的解决方法我可以想到的是以某种方式迫使所有的流程,同意在那里地图共享存储器,以便实的指针,而不是偏移量可以储存在mmap会区域。是否有任何方式做到这一点吗?我看到MAP_FIXED在mmap文件,但我不知道怎么我可以挑选一个地址,这将是安全的。
编辑:使用内联会和"锁定"前缀在x86也许这有可能建立一个"增量ptr X与偏Y值Z"?相当于选择在其他架构?还没有写很多会不知道如果需要说明的存在。
解决方案
在低级别的x86原子inctruction可以做所有这棵树下步骤:
- x=读p
- y=x+偏增量
- refcount在y
// mov edi, Destination mov edx, DataOffset mov ecx, NewData @Repeat: mov eax, [edi + edx] //load OldData //Here you can also increment eax and save to [edi + edx] lock cmpxchg dword ptr [edi + edx], ecx jnz @Repeat //
其他提示
这是微不足道的UNIX系统;只要使用共享存储器功能:
shgmet,shmat,shmctl,shmdt
void*shmat(int shmid,const void*shmaddr,int shmflg);
shmat()重视的共享存储器 分段确定的shmid的 地址空间呼吁进程。该附加指定的地址 shmaddr一下 标准:
如果shmaddr是空的,对系统选择 一个适合(不使用)地址 附加部分。
只有指定你自己的地址;例如 0x20000000000
如果你shmget()使用相同的密钥和大小的每一过程中,你将得到同样的共享内存段。如果你shmat()在同一地址,虚拟地址将同在所有进程。核不在乎什么地址范围内使用,只要它没有冲突,无论它通常分配的东西。(如果你离开了地址,可以看到一般的区域,它喜欢把事情;此外,检查的地址上堆和返回malloc()/新[].)
在Linux上,确保根集SHMMAX在/proc/sys/核心/shmmax到一个数量足够大,以容纳共享内存段(默认值是32MB).
作为原子操作,可以获得他们都来自Linux内核来源,例如
包括/asm-x86/atomic_64.h
/*
* Make sure gcc doesn't try to be clever and move things around
* on us. We need to use _exactly_ the address the user gave us,
* not some alias that contains the same information.
*/
typedef struct {
int counter;
} atomic_t;
/**
* atomic_read - read atomic variable
* @v: pointer of type atomic_t
*
* Atomically reads the value of @v.
*/
#define atomic_read(v) ((v)->counter)
/**
* atomic_set - set atomic variable
* @v: pointer of type atomic_t
* @i: required value
*
* Atomically sets the value of @v to @i.
*/
#define atomic_set(v, i) (((v)->counter) = (i))
/**
* atomic_add - add integer to atomic variable
* @i: integer value to add
* @v: pointer of type atomic_t
*
* Atomically adds @i to @v.
*/
static inline void atomic_add(int i, atomic_t *v)
{
asm volatile(LOCK_PREFIX "addl %1,%0"
: "=m" (v->counter)
: "ir" (i), "m" (v->counter));
}
64位的版本:
typedef struct {
long counter;
} atomic64_t;
/**
* atomic64_add - add integer to atomic64 variable
* @i: integer value to add
* @v: pointer to type atomic64_t
*
* Atomically adds @i to @v.
*/
static inline void atomic64_add(long i, atomic64_t *v)
{
asm volatile(LOCK_PREFIX "addq %1,%0"
: "=m" (v->counter)
: "er" (i), "m" (v->counter));
}
我们的代码类似于你的问题的说明。我们利用存储器的映射文件、偏移和文件锁。我们还没有找到一种替代方法。
你不应该害怕做了一个地址在随机的,因为核心只会拒绝的地址它不象(那些冲突)。看到我的 shmat()
回答以上,使用 0x20000000000
与mmap:
void*mmap(void*addr,位置length,int prot,int flags,int fd,off_t offset);
如果地址不是空,然后核 把它作为一个提示在哪里 地方的映射;在Linux, 映将在下一个 更高的页边界。地址 新的映射是返回作为 结果的呼吁。
该标志说法是否确定 更新的映射是可见的 其他进程映相同的 区域,以及是否更新 通过对基础 文件。这种行为是确定的 包括完全一下 值标志:
MAP_SHARED 分享这个的映射。更新的映射是可见的 其他进程,这个地图 文件中,都进行过的 基础文件。该文件可能不 实际上进行更新,直至msync(2)或 munmap()。
错误。
EINVAL 我们不喜欢的地址,长度,或 抵消(例如,它们太大,或 不结盟页面上的边界)。
添加偏移的指针不会创造潜在的竞赛,它已经存在。由于至少既没有手臂也不x86能自动读取指然后接入存储器是指你需要保护的指访问锁无论是否添加偏移。