「これは私が見ていることを説明していますが、いくつか質問があります。」
「何かがすでに特定のアドレスにマップされているかどうかを検出する方法はありますか? /proc/maps にアクセスせずに?」
はい、MAP_FIXED なしで mmap を使用します。
「重複するページが見つかった場合に強制的に mmap を失敗させる方法はありますか?」
どうやらそうではありませんが、mmap が要求されたアドレス以外のマッピングを返す場合は、mmap の後に単に munmap を使用してください。
MAP_FIXEDなしで使用すると、Linux と Mac OS X の両方で (そして他の場所でもあると思われます) mmap は、[アドレス、アドレス + 長さ] の範囲内に既存のマッピングが存在しない場合、アドレス パラメーターに従います。したがって、 mmap が指定したアドレスとは異なるアドレスでマッピングに応答する場合、その範囲にマッピングが既に存在し、別の範囲を使用する必要があると推測できます。mmap は通常、address パラメータを無視すると、非常に高いアドレスでマッピングに応答するため、単に munmap を使用して領域のマッピングを解除し、別のアドレスで再試行します。
mincore を使用してアドレス範囲が使用されているかどうかを確認することは、時間の無駄であるだけでなく (一度に 1 ページずつプローブする必要があります)、機能しない可能性があります。古い Linux カーネルは、ファイル マッピングに対してのみ適切に mincore を失敗させます。MAP_ANON マッピングについては、何も答えません。しかし、私が指摘したように、必要なのは mmap と munmap だけです。
Smalltalk VM 用のメモリ マネージャーを実装するこの演習を行ったところです。sbrk(0) を使用して、最初のセグメントをマップできる最初のアドレスを見つけ、mmap と 1Mb のインクリメントを使用して、後続のセグメント用のスペースを検索します。
static long pageSize = 0;
static unsigned long pageMask = 0;
#define roundDownToPage(v) ((v)&pageMask)
#define roundUpToPage(v) (((v)+pageSize-1)&pageMask)
void *
sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize)
{
char *hint, *address, *alloc;
unsigned long alignment, allocBytes;
if (pageSize) {
fprintf(stderr, "sqAllocateMemory: already called\n");
exit(1);
}
pageSize = getpagesize();
pageMask = ~(pageSize - 1);
hint = sbrk(0); /* the first unmapped address above existing data */
alignment = max(pageSize,1024*1024);
address = (char *)(((usqInt)hint + alignment - 1) & ~(alignment - 1));
alloc = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto
(roundUpToPage(desiredHeapSize), address, &allocBytes);
if (!alloc) {
fprintf(stderr, "sqAllocateMemory: initial alloc failed!\n");
exit(errno);
}
return (usqInt)alloc;
}
/* Allocate a region of memory of at least size bytes, at or above minAddress.
* If the attempt fails, answer null. If the attempt succeeds, answer the
* start of the region and assign its size through allocatedSizePointer.
*/
void *
sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(sqInt size, void *minAddress, sqInt *allocatedSizePointer)
{
char *address, *alloc;
long bytes, delta;
address = (char *)roundUpToPage((unsigned long)minAddress);
bytes = roundUpToPage(size);
delta = max(pageSize,1024*1024);
while ((unsigned long)(address + bytes) > (unsigned long)address) {
alloc = mmap(address, bytes, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (alloc == MAP_FAILED) {
perror("sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto mmap");
return 0;
}
/* is the mapping both at or above address and not too far above address? */
if (alloc >= address && alloc <= address + delta) {
*allocatedSizePointer = bytes;
return alloc;
}
/* mmap answered a mapping well away from where Spur prefers. Discard
* the mapping and try again delta higher.
*/
if (munmap(alloc, bytes) != 0)
perror("sqAllocateMemorySegment... munmap");
address += delta;
}
return 0;
}
これはうまく機能しているように見え、既存のマッピングをスキップしながら昇順のアドレスにメモリを割り当てます。
HTH