Why Does Munmap Needs a Length as Parameter

Why does munmap needs a length as parameter?

One can map, say, 5 pages and later unmap one of them. And information about what pages to unmap is passed as address and length where the length is a multiple of page size.

Is the length in mmap a number of bytes or a number of pages?

The length parameter is in bytes. The Linux man page does not say this explicitly, but the POSIX spec says (emphasis mine):

The mmap() function shall establish a mapping between the address space of the process at an address pa for len bytes to the memory object represented by the file descriptor fildes at offset off for len bytes.

It is possible to use mmap as a way to allocate memory (you'll want to use MAP_ANONYMOUS or else map the /dev/zero device), but it's not in general a good direct replacement for malloc:

  • Mappings will always be made in page units (so the system will round up length to the next multiple of the page size), so it's very inefficient for small allocations.

  • You can't pass pointers returned by mmap to realloc or free (use mremap and munmap instead).

  • munmap actually returns memory to the system, whereas free may potentially keep it assigned to your process and just mark it available for use by future calls to malloc. This has pros and cons. On the one hand, if you know you will not be needing that memory in the future, it is nice to let the system have it back. On the other hand, every mmap/munmap requires a system call, which is relatively slow, whereas malloc may be able to allocate previously freed memory that already belongs to your process without a system call.

The 'length` parameter of msync does not work

Thanks for the people inspiring me in the comments, sorry I did a poor reading of the manual.

The msync cannot extend the size you mapped, you cannot mmap more size than the file size neither.

So here is the idea:

  1. increase the filesize
  2. unmap
  3. remmap
  4. msync the remapped f with new content

There for I implemented the resize_remap function

result_t file_remap_resize(int* fd, char** f, int new_size){
if(ftruncate(*fd, new_size) == -1){
printf("failed to resize\n");
return ERR_CODE;
}
if(munmap(*f, sizeof(*f)) == -1){
printf("failed to unmap\n");
return ERR_CODE;
}
*f = (char*)mmap (0, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(!f){
printf("failed to remap\n");
return ERR_CODE;
}
return SUCCESS
}

Then the program is like:

char* rendered = cJSON_Print(root);
file_remap_resize(&fd, &f, strlen(rendered));
memcpy(f, rendered, strlen(rendered));
msync(f, strlen(renderer), MS_SYNC);

and it works!

The modified file can be flushed back into the disk!

linux munmap not working (or at least not working instantly)

when I tried to mmap(address_overlapping_with_what_I_tried_to_munmap, ...) I did not get the address I requested.

Read mmap(2) carefully again:

If addr is NULL, then the kernel chooses the address at which to
create the mapping; this is the most portable method of creating a
new mapping. If addr is not NULL, then the kernel takes it as a hint
about where to place the mapping

and later:

MAP_FIXED
Don't interpret addr as a hint: place the mapping at exactly
that address. addr must be a multiple of the page size.

So if you really need to have the address passed to mmap as a useful address, you should use MAP_FIXED (with caution, since it can overwrite and "crush" a very useful pre-existing memory map).

In other words, I understand that the address to mmap should better be 0, unless you say MAP_FIXED. Otherwise, it is just a "hint" and I don't know what that practically means (especially with ASLR).

AFAIU, the /proc/self/maps or /proc/1234/maps reflect instantaneously the kernel's perception of your process virtual address space. Of course, you need to open that pseudo-file, read it quickly and sequentially, and close it as soon as possible (don't keep a file descriptor to an open-ed /proc/self/maps for several seconds). What happens if your process is mmap-ing or munmap-ing (or changing its address space in some other way) between the open & the close of that /proc/*/maps file is IMHO undefined behavior -or at least unspecified behavior- on which you should not depend.

But mmap & munmap are effective as soon as that system call is returned from; there is no "delay" in changing the virtual address space. The virtual memory machinery may give some delay (e.g. when fetching some page from some remote network ...) but that delay is not accessible to applications (the process would be in D state). Read also about thrashing.

C: Warning about munmap to struct

Your code with the cast is correct and neccessary.

Is it possible to perform munmap based on information in /proc/self/maps?

Yes it is possible. I had a closer look at my code; there was something I was misunderstanding when it came to passing the start address of the mapping to munmap. I had initially read the start address into an unsigned long long and for some reason, I was converting this value into a hex string instead of just casting to a void pointer when calling munmap. In essence:

/* Values assigned here are really read from /proc/self/maps */
unsigned long long vm_start = 140013986873344;
unsigned long long vm_end = 140013986877440;

unsigned long long length = vm_end - vm_start;
munmap((void *)vm_start, length);


Related Topics



Leave a reply



Submit