In Linux user space: suppose that I allocated a 3MiB continuous memory ABC
, where A
, B
and C
are 1MiB each. Is there a way to access AC
as a single continuous 2MiB memory somehow (a kind of user space MMU)?
Background: my scenario is to model an ASIC chip in C. The chip has a 3MiB memory, and two components in the chip can access the same memory hardware, but with different address maps. To model that in C, one way could be duplicating the 3MiB memory for the two components, and adding some synchronization mechanism between the two memory copies. The other way could be just having a single copy of 3MiB memory, one component accesses the memory as it is, and for the other component, adding a kind of wrapper to do the address translation. I just want to know if there is a neater way, avoiding the memory duplication and the address translation wrapper...
PS: according Maxim's answer, I wrote a simple test program, and it just works. It seems I should not use MAP_ANONYMOUSE
flag for my purpose. Also, according to mmap()
manual, for MAP_FIXED
to work properly, the alignment of C
should be multiple of PAGE_SIZE
on the target platform (mine x86_64 Ubuntu). I paste the code below just in case...
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#define SZ_A (64 * 1024) // A
#define SZ_B (204 * 1024) // B
#define SZ_C (4 * 1024) // C
#define SZ_ABC (272 * 1024) // ABC
#define SZ_AB (268 * 1024) // AB
#define SZ_AC (68 * 1024) // AC
const char shm_name[] = "/shm_name1";
unsigned char* p_abc = NULL;
unsigned char* p_ac = NULL;
#define MMAP_PROT (PROT_READ | PROT_WRITE)
#define USE_MAP_ANONYMOUSE 0
#if USE_MAP_ANONYMOUSE
#define MMAP_FLAG (MAP_SHARED | MAP_ANONYMOUS)
#define MMAP_FLAG_FIXED (MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED)
#else
#define MMAP_FLAG (MAP_SHARED)
#define MMAP_FLAG_FIXED (MAP_SHARED | MAP_FIXED)
#endif
int main(void)
{
int fd = - 1;
void* p = NULL;
fd = shm_open(shm_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd == - 1) {
printf("shm_open() fail\n");
return - 1;
}
if (0 != ftruncate(fd, SZ_ABC)) {
printf("ftruncate() fail\n");
return - 1;
}
if (0 != shm_unlink(shm_name)) {
printf("shm_unlink() fail\n");
return - 1;
}
p_abc = (unsigned char*)mmap(NULL, SZ_ABC, MMAP_PROT, MMAP_FLAG, fd, 0);
if (p_abc == (unsigned char*) -1) {
printf("p_abc = mmap(NULL) fail\n");
p_abc = NULL;
goto EXIT;
}
p_ac = (unsigned char*)mmap(NULL, SZ_AC, MMAP_PROT, MMAP_FLAG, fd, 0);
if (p_ac == (unsigned char*) -1) {
printf("p_ac = mmap(NULL) fail\n");
p_ac = NULL;
goto EXIT;
}
p = mmap(p_ac + SZ_A, SZ_C, MMAP_PROT, MMAP_FLAG_FIXED, fd, SZ_AB);
if (p == MAP_FAILED || p != (void*)(p_ac + SZ_A)) {
printf("mmap(MAP_FIXED) fail\n");
p = NULL;
goto EXIT;
}
close(fd);
printf("mmap() ok:"
"\np_abc=0x%" PRIxPTR
"\n p_ac=0x%" PRIxPTR
"\n p=0x%" PRIxPTR "\n",
(uintptr_t)p_abc, (uintptr_t)p_ac, (uintptr_t)p);
// test
memset(p_abc, 0xab, SZ_AB);
memset(p_abc + SZ_AB, 0x0c, SZ_C);
// should be: ab, 0c
printf("sm4: %02x, %02x\n", p_ac[0], p_ac[SZ_A]);
memset(p_ac, 0x0c, SZ_AC);
// should be: 0c, 0c
printf("sm0: %02x, %02x\n", p_abc[0], p_abc[SZ_AB]);
EXIT:
if (p) munmap(p, SZ_C);
if (p_ac) munmap(p_ac, SZ_AC);
if (p_abc) munmap(p_abc, SZ_ABC);
return 0;
}
suppose that I allocated a 3MiB continuous memory
ABC
, whereA
,B
andC
are 1MiB each. Is there a way to accessAC
as a single continuous 2MiB memory somehow (a kind of user space MMU)?
You can create a memory mapped file of 3MiB size with shm_open
(and, optionally, immediately shm_unlink
it). Then map that file into the process address space multiple times using different offsets and sizes.
One note, to map AC
contiguously, you first need to mmap
2MiB of anonymous memory to reserve a contiguous block of address space. Then mmap
the first half of it to A
and the second half to C
using MAP_FIXED
flag.
Pseudo-code:
// Create an anonymous shared memory file.
int fd = shm_open(filename);
shm_unlink(filename);
ftruncate(fd, 3MiB);
// Map A and C contiguously.
void* ac = mmap(NULL, 2MiB, ..., fd, 0MiB); // maps AB part of ABC.
mmap(ac + 1MiB, 1MiB, ... | MAP_FIXED, fd, 2MiB); // remaps C part over B part (B gets unmapped).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With