16

Playing around with mmap for the fun of it, I have the following code:

(.. snip ..)
fd = open("/home/me/straight_a.txt", O_RDONLY);
if (fd == -1) {
    perror("open");
    exit(1);
}

m = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0);

if (m == MAP_FAILED) {
    perror("mmap");
    exit(1);
}

printf("m is %p\n", m);

printf("*m = %c\n", *m);
printf("*(m+1) = %c\n", *(m+1));
(.. snip ..)

This works as expected. But before I got to this, I tried...

m = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);

... and mmap errored out with:

mmap: Permission denied

In general, what's the difference between the two flags (man page isn't to generous on this subject)? What sort of permission (and where) am I missing?

EDIT

Like it usually happens.. partially figured it out.

Turns out open needed an O_RDWR flag.

So, am I correct to assume that:

  • MAP_PRIVATE - changes are made in memory only, not saved to disk?
  • MAP_SHARED - changes would be saved to disk...

... but I'm not saving anything to disk anywhere, I thought? Just operating on memory.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
ntl0ve
  • 1,896
  • 3
  • 19
  • 25
  • 3
    `, O_RDONLY);` The file is read-only. It cannot be used as a backing storage for a `PROT_READ|PROT_WRITE` mmap()ed area because it is not writable. The `MAP_PRIVATE` mapping does not need to write to the file (the file is only used for reading, presumably by COW) Note: I would not expect tilde expansion to work for open(2). It would amaze me if it works. – wildplasser Mar 01 '12 at 16:12
  • Right, it was just a quick way to get the real base dir out of there, didn't even think about it. Thanks, fixed for the sake of correctness. – ntl0ve Mar 01 '12 at 19:42

2 Answers2

21

You opened the file in read-only mode. Then you attempted to mmap part of it in read/write mode with MAP_SHARED set. In this context, MAP_SHARED implies that if you write to the mmap'd region your changes will be committed back to the mapped file itself. You can't do this because you opened the file in read-only mode.

MAP_PRIVATE works because writes to the mmap'd region are not committed back to the original file. When you write to the region, the pages that were written to are copied to a different region of memory, possibly backed by swap space.

Dharmit
  • 5,498
  • 2
  • 27
  • 30
karunski
  • 3,980
  • 2
  • 17
  • 10
10

Writes to a MAP_SHARED segment are carried through to the underlying file. You opened the file with O_RDONLY, which conflicts with the PROT_WRITE flag, thereby preventing MAP_SHARED from being able to write back to the file.

MAP_PRIVATE does not carry writes back to the underlying file, so the fact that you opened the file O_RDONLY is not an issue.

Tim
  • 336
  • 2
  • 8
  • When are my changes in memory commited to disk? Do I need to call, or is it automatic? If so, any buffering? – ntl0ve Mar 01 '12 at 16:17
  • Short anwer: you don't know. Anything between now and eternity. munmap() may flush the disk buffers, depending on the phase of the moon. – wildplasser Mar 01 '12 at 16:20
  • 2
    Got it. Also found `msync`, which promises to force a flush/sync. Thanks. – ntl0ve Mar 01 '12 at 16:23
  • @wildplasser In my experience munmap() never flushes buffers. Thus you can use mmap/munmap to implement address space windowing when physical memory exceeds virtual memory address space. – karunski Mar 01 '12 at 18:20
  • 1
    That is correct. It just leaves them on the write queue (if they are dirty) MAP_SYNC will cause munmap() to block until they are actually written (just like msync()). That is the phase of the moon part of the story. – wildplasser Mar 01 '12 at 18:50
  • @wildplasser What OS provides this MAP_SYNC option? I don't see it in linux or OS X. – karunski Mar 02 '12 at 13:38
  • Sorry I was confused by MS_SYNC. {MS_ASYNC, MS_SYNC, MS_INVALIDATE} are flag arguments values for msync()). For mmap() there are no corresponding options. munmap() allows {MS_SYNC or MS_ASYNC} to be specified. The real problem still is: you cannot *prevent* anything to be written to the backing storage. Between mmap() and munmap() the order of page writes is undefined, only a bit of order can be imposed by intervening msync()s. – wildplasser Mar 02 '12 at 13:53
  • Munmap doesn't take a flags argument. I think that's only msync. As an interesting anecdote, Freebsd does seem to expose a MAP_NOSYNC that does prevent anything from being written to backing storage. – karunski Mar 02 '12 at 15:27