How do I choose a fixed address for mmap? - c

mmap() can be optionally supplied with a fixed location to place the map. I would like to mmap a file and then have it available to a few different programs at the same virtual address in each program. I don't care what the address is, just as long as they all use the same address. If need be, the address can be chosen by one of them at run time (and communicated with the others via some other means).
Is there an area of memory that Linux guarantees to be unused (by the application and by the kernel) that I can map to? How can I find one address that is available in several running applications?

Not really, no. With address space randomisation on modern linux systems it is very hard to guarantee anything about what addresses may or may not be used.
Also, if you're thinking of using MAP_FIXED then be aware that you need to be very careful as it will cause mmap to unmap anything that may already be mapped at that address which is generally a very bad thing.
I really think you will need to find another solution to your problem...

Two processes can map a shared memory block to the same virtual address using shm_open() and mmap(); that is, the virtual address returned from mmap() can be the same for both processes. I found that Linux will by default give different virtual addresses to different processes for the same piece of shared memory, but that using mmap() with MAP_FIXED will force Linux to supply the same virtual address to multiple processes.
The process that creates the shared memory block must store the virtual address somewhere, either within the shared memory, in a file, or with some other method so that another process can determine the original virtual address. The known virtual address is then used in the mmap() call, along with the MAP_FIXED flag.
I was able to use the shared memory to do this. When doing so, the "golden" virtual address is stored within the shared memory block; I made a structure that contains a number of items, the address being one of them, and initialized it at the beginning of the block.
A process that wants to map that shared memory must execute the mmap() function twice; once to get the "golden" virtual address, then to map the block to that address using the MAP_FIXED flag.
Interestingly, I'm working with an embedded system running a 2.6 kernel. It will, by default, supply the same virtual address to all mmap() calls to a given file descriptor. Go figure.
Bob Wirka

You could look into doing a shared memory object using shmget(), shmat(), etc. First have the process that obtains the right to initialize the shared memory object read in your file and copy it into the shared memory object address space. Now any other process that simply gets a return shared memory ID value can access the data in the shared memory space. So for instance, you could employ some type initialization scheme like the following:
#include <sys/shm.h>
#define KEYVALUE 1000 //arbitrary value ... just needs to be shared between your processes
int file_size
//read your file and obtain its size in bytes;
//try to create the shared memory object
int shared_mem_id;
void* shared_mem_ptr = NULL;
if ((shared_mem_id = shmget(KEYVALUE, file_size, S_IRUSR | S_IWUSR IPC_CREAT | IPC_EXCL)) == -1)
{
if (errno == EEXIST)
{
//shared memory segment was already created, so just get its ID value
shared_mem_id = shmget(KEYVALUE, file_size, S_IRUSR | S_IWUSR);
shared_mem_ptr = shmat(shared_mem_id, NULL, 0)
}
else
{
perror("Unable to create shared memory object");
exit(1);
}
}
else
{
shared_mem_ptr = shmat(shared_mem_id, NULL, 0);
//copy your file into shared memory via the shared_mem_ptr
}
//work with the shared data ...
The last process to use the shared memory object, will, just before destroying it, copy the modified contents from shared memory back into the actual file. You may also want to allocate a structure at the beginning of your shared memory object that can be used for synchronization, i.e., there would be some type of "magic number" that the initializing process will set so that your other processes will know that the data has been properly initialized in the shared memory object before accessing it. Alternatively you could use a named semaphore or System V semaphore to make sure that no process tries to access the shared memory object before it's been initialized.

Related

IPC_CREAT - Should it simply be seen as an "empty file" in the "memory"?

This is how a shared memory segment is created:
memory_id = shmget(key, memory_size, IPC_CREAT | 0755);
the 0755 portion tells me perhaps this is like creating a "file" on a file system that exists in the memory only.
is it correct to look at it in such way ?
does this also mean.. i must store data to it as if it is simply a file ?
in other words there can not be such thing as.. creating 2 files inside a file.
i must simply concatenate my strings, seperate them with a special character such as a pipe symbol. and write them all at once to the file that has been created in the memory ?
Yes, except for something... you cannot write(2) to a shared memory segment. You have to shmat(2) to attach it to your process memory and then you can access it (as if it were a result from malloc(2) ---but it isn't, you cannot free(3) it anyway)
When you are finished, you can simply shmdt(2) as if you were freeing it (this doesn't free it as a system resource although, it continues to conserve its contents for another process to use them)
The 0755 is, indeed, the permission bits, as this resource is managed as a file. When you shmat(2) it, permission bits are checked for access and you will get an error in case you don't have enough access to the resource. Execute bits are not used, so your permissions should use 0644 instead (execute bits are reserved, so they should be 0)
Shared memory segments are an expensive resource, as they consume system main memory (despite of them being swapped out, in case of need) and they don't disappear when the process that created them exit(2)s, so be careful, and they are available only until the system is rebooted.
Beware though that if you create references to places in the shared memory segment, they refer to private, virtual memory addresses in the virtual space of the writing process... if the memory segment is attached to the memory of another process, all those memory addresses will be invalid (or must be offseted to match the other process virtual memory space)

Is there a system call in linux to reserve virtual address space (not memory, just address space)

I have a user space "platform" forking different processes. All these processes start executing a platform plat_init() function, and then run some other application code (which is not mine. i.e. I cannot change this code).
At some point in time some of these processes may do a plat_shared_mem_alloc() to allocate shared memory. This function returns a handle H (one handle per shared memory block allocated at each call). Another function, plat_get_shared_memory_address(H) can be called to retrieve the address (in the process virtual space) of H.
H can be sent to other linux processes (using usual IPC).
I would like any call to plat_get_shared_memory_address(H) made by any processes to return the same address (for the same handle H).
In other words, I want to map the shared physical memory to the same virtual address in all processes using it, regardless on when the mapping is done.
I thought the plat_shared_mem_alloc() could call shm_open() to create a "file" in the file system, and I am aware mmap() has the MAP_FIXED flag to force the virtual address of the mapping.
But nothing guarantee that if a process P1 maps a shared memory handle at address A, then the same address A is/will be available in another process P2 address space. Maybe P2's application's code has already mapped something at address A before calling plat_get_shared_memory_address(H), and mmap() will fail.
So I am thinking of blocking some address space in each process during plat_init() call (which I know comes first). and using some of that address space when needed for mmap().
In other words, is there a system call to block (reserve) some virtual address space of a process (without allocation any memory at this time), so I could, later on, if needed, map things at the same address in my different processes?

Shared memory variables in C, extern keyword

If I create shared memory variables in C, where are they placed (heap / stack / data section / ...)? I am using 64 bit Ubuntu with gcc-4.8 and the compilerflag -m32 for 32bit and this code:
segment_id = shmget (IPC_PRIVATE, shared_segment_size,
IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
shared_memory = (char*) shmat (segment_id, 0, 0);
printf ("shared memory attached at address |%10p\n", shared_memory);
I get the following output for this:
shared memory attached at address |0xf76de000
However, valgrind gives this:
shared memory attached at address | 0x4039000
Why? Do shared variables have anything to do with the extern keyword? If another process wants to use the data, can he choose where to attach the memory?
Think of shared memory as process-independent (take that with a grain of salt) memory chunks that are managed by the kernel. When you call shmat(), think of it as a mmap()-like operation that creates a mapping between the address space of the current process and the shared segment. In fact, you can force the address to be a particular value with the second argument to shmat(). But if you do not, it is automatically generated. The actual value will depend on a number of factors, the current memory mapping of the process in particular.
Thus you have one mapping when you run the process standalone, and another when using valgrind. For all intents and purposes you can consider this a heap address - it is not going to be a part of your stack space. But better, just forget the stack and heap distinction and think of it as some memory you got that you can play with as long as you do it by the rules (do not overwrite the boundaries, respect permissions),

how to unmap the memory which is mapped using remap_pfn_range()

There are multiple approaches to map kernel memory to user space.
Some say use splice(), mmap(), etc.
I am calling mmap() with a descriptor of our own pseudo char device file like '/dev/mem'.
When calling mmap() with with our own pseudo char device file, internally in mmap registered file_operation function pointer, we can invoke remap_pfn_range() for mapping memory..
Now that process might have got terminated/killed/clean exit().
How to remove those mappings from kernel space. I am working on ARMv7-A.
Can anyone explain what happens about these memory mappings when the process gets killed/terminated? Does kernel remove the mappings by itself or do we need to unmap explicitly?
You can handle it via release when last copy exits.
struct file_operations {
...
int (*release) (struct inode *, struct file *);
...
}
From LDD
This operation is invoked when the file structure is being released.
Like open, release can be missing.[ Note that release isn't invoked
every time a process calls close. Whenever a file structure is shared
(for example, after a fork or a dup), release won't be invoked until
all copies are closed. If you need to flush pending data when any copy
is closed, you should implement the flush method.
mmap maps an external (to the process) memory space to the virtual address space of the process calling it. Memory maybe a shared memory segment, a file... Unlike the physical memory segment it maps to, mmap just creates a "link" to that segment, and returns an address that can be seen and used from the calling process.
When the mmap calling process terminates (naturally, killed..) the mappings it created are automatically unmapped.
The physical memory region that was mapped, however, and that may be used by other processes (or no), remains available.
man mmap
You may close the mapping from the program before it dies,
int munmap(void *addr, size_t length);

Memory Management for Mapped Data in Shared Memory Segments

I'm working on a project in C that uses shared memory for IPC on a Linux system. However, I'm a little bit confused about memory management in these segments. I'm using the POSIX API for this project.
I understand how to create the shared segments, and that these persist until a reboot if you fail to properly remove them with shm_unlink(). Additionally, I understand how to do the actually mapping & unmapping with mmap and munmap respectively. However, the usage of these operations and how it affects the stored data in these shared segments is confusing me.
Here is what I'm trying to properly understand:
Lets say I create a segment using shm_open() with the O_CREAT flag. This gives me a file descriptor that I've named msfd in the below example. Now I have a struct that I map into that address space with the following:
mystruct* ms = (mystruct*)mmap(NULL, sizeof(mystruct), PROT_READ | PROT_WRITE, MAP_SHARED, msfd, 0);
//set the elements of the struct here using ms->element = X as usual
Part 1)
Here's where my confusion beings. Lets say that this process is now done accessing that location since it was just setting data for another process to read. Do I still call munmap()?
I want the other process to still have access to all of this data that the current process has set. Normally, you wouldn't call free() on a malloc'ed pointer until its use is no longer needed permanently. However, I understand that when this process exits the unmapping happens automatically anyway. Is the data persisted inside the segment, or does that segment just get reserved with it's allotted size and name?
Part 2)
We're now in the process of the other application that needs to access and read from that shared segment. I understand that we now open that segment with shm_open() and then perform the same mapping operation with mmap(). Now we have access to the structure in that segment. When we call munmap() from this process (NOT the one that created the data) it "unlinks" us from that pointer, however the data is still accessible. Does this assume that process 1 (the creator) has NOT called munmap()?
Is the data persisted inside the segment,
Yes.
does that segment just get reserved with it's allotted size and name?
Also yes.
Does this assume that process 1 (the creator) has NOT called munmap()?
No.
The shared memory gets created via shm_create() (as being taken from available OS memory) and from this moment on it carries whichever content had been written into until it is given back to the OS via shm_unlink().
shm_create() and shm_open() act system oriented, in terms of the (shared) memory being a system (not process) specific resource.
mmap() and unmap() act process oriented, that is map and unmap the system resource shared memory into/out-of the process' address space.

Resources