保存しておくことはできポインタの共有メモリを用いずにオフセット?
-
21-09-2019 - |
質問
が共有メモリを利用して、各プロセスが受、共有の地域の異なる地域のそれぞれのアドレス空間です。これを保管する場合はポインタの共有、地域に必要なもの 店舗としてオフセット 開始の共有。残念ながら、この複雑なものに使用原子の指示などねばを書く ロックフリーでアルゴリズム).例えば、言いてチームの参照対象のノードの共有メモリによる単一のライター。(筆者は定期的に原子的に更新を指すポインター'p'のポイントに有効なノードのプラスの参照カウント。読者のために原子的に書き込み"p"でポイントのノード(構造体)の最初の要素がある。以来p常にポイントの有効なノードincrementingる、参照カウントが安全に、安全に逆参照'p'のアクセスの会員をいいます。しかし、このすべてにしているときだけすべてが同じアドレス空間です。た場合のノードで、'p'のポインタが格納される共有メモリ、お客様がレースの条件:
- x=p読む
- y=x+オフセット
- 増refcount y
中2,pされた場合には、xなくなった時点で有効なノードです。の回避策して私が考えられることはいは何らかの強制力はすべてのプロセスの合意に地図を共有メモリ、くしのポインタでオフセットとして保存することもでき受けいた。はありません。見MAP_FIXEDの受文書がかかるのかを明らかにするに住所を有する安心です。
編集:使用インライン組立とロック"という接頭辞が付x86かもしれないがると"増分値ptr Xとoffset 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がNULLの場合、システム選択 で適切な(未使用)のアドレス セグメントを添付するます。
ただ、ここでは、独自のアドレスを指定します。例えばの0x20000000000 のの
<強いです> あなたはすべてのプロセスで同じキーとサイズを使用して)(たshmget場合はは、あなたが同じ共有メモリ・セグメントを取得します。あなたが同じアドレスに()にshmat場合、仮想アドレスは、すべてのプロセスで同じになります。カーネルは、限り、それは通常のものを割り当ててどことの競合をしないよう、あなたが使用しているものアドレス範囲を気にしません。 (あなたがアドレスを除外した場合、あなたはそれが物事を置くのが好きという一般的な地域を見ることができます;)スタック上にも、チェックアドレス及び(malloc関数から返された/新しい[])
上のLinuxは、必ずルートはSHMMAX(デフォルトは32メガバイトである)共有メモリ・セグメントを収容するのに十分な大きさの数には、/ proc / sys / kernel / shmmaxの中で設定します。
アトミック操作として、あなたは、例えば、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
と受け:
void*受(void*addr,size_t length,int prot,int flags,int fd off_tオフセット);
場合はaddrがNULLでない場合は、カーネルの 私にはちょっと物足りなく感じてヒントに 場所をマッピングLinux、 マッピングすることができ、次 高ページの境界線。の住所 新しいマッピングとして返されますの 呼び出しの結果.
のflags引数かどうかを判定し 更にマッピングの目に見える その他のプロセスのマッピングと同じ 地域やかアップデート 行を通じて基本となる ファイルです。この動作によって決 どう次の 値がフラグ:
MAP_SHARED このマッピングしました。更にマッピングの目に見える その他のプロセスにマップするこ ファイル、あれば教えてください。、 配下のファイルです。いただけない場合がありま 実際に更新されまmsync(2)または munmap()が呼び出されます。
誤差
EINVAL 嫌いaddr、長さ、または オフセットなど、大きすぎて、 なの揃ったページの境界).
レースの可能性を作成していないポインタにオフセットを追加し、それがすでに存在しています。少なくとも、ARMやx86のどちらもアトミックにポインタを読むことができるので、あなたにそれが参照するメモリへのアクセスロック付きポインタへのアクセスを保護する必要があるにかかわらず、あなたが追加するかどうかのオフセットます。