CodeRigor

FREE LESSON

Virtual memory & mapped file windows

Map an unaligned file slice with page-aligned mmap, separate ownership from spans, transfer mappings safely, and understand MAP_PRIVATE, page faults and SIGBUS after truncation.

Memory mapping adds a file-backed region to a process's virtual address space. Access can fault pages in on demand rather than copying the whole file up front. This does not make every workload faster: page-fault cost, access pattern, file changes and storage policy still matter. The lesson grades correctness, not a claim about hardware speed.

Map pages, expose bytes

mmap requires an aligned file offset. A caller's requested slice need not be aligned. Compute the preceding page boundary, retain the difference as delta, and map delta + requested_length bytes. The public span begins at base+delta, but ownership remains the original base and mapping length.

Aligned mapping, adjusted public view
Toy page size 16: file offset 23 needs mapping offset 16, delta 7 and mapping length 12 for a five-byte view. Real code queries the actual page size.
Aligned mapping, adjusted public view
Toy page size 16: file offset 23 needs mapping offset 16, delta 7 and mapping length 12 for a five-byte view. Real code queries the actual page size.
C++
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
    char name[]="/tmp/unix-check-XXXXXX"; int fd=::mkstemp(name); assert(fd>=0); ::unlink(name);
    assert(::write(fd,"abcdef",6)==6); assert(::lseek(fd,0,SEEK_SET)==0);
    auto* p=static_cast<char*>(::mmap(nullptr,6,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0));assert(p!=MAP_FAILED);
    p[0]='X';char file=0;assert(::pread(fd,&file,1,0)==1);std::printf("%c %c\n",p[0],file);assert(::munmap(p,6)==0);::close(fd);
}

The build queries _SC_PAGESIZE at runtime. Validate the requested range with fstat and subtraction, check length addition, and keep empty ranges separate: passing length zero to mmap is not how to represent an empty C++ view.

C++
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <unistd.h>

int main() {
    auto page=::sysconf(_SC_PAGESIZE);assert(page>0);long offset=page+7,length=5;
    long aligned=offset/page*page,delta=offset-aligned;std::printf("%ld %ld\n",delta,length+delta);
}

Ownership and borrowed spans

A successful mapping has a lifetime separate from the descriptor used to create it. Closing that descriptor is allowed. Destroying or resetting the mapping invalidates every borrowed span, so never return a view into a temporary owner. Move construction empties the source; move assignment first releases old state. munmap uses the original base, not the adjusted span pointer.

A span borrows; the mapping owner releases
The returned bytes view points into an owned mapping. Closing the original file descriptor is allowed; resetting the mapping invalidates all borrowed views.
A span borrows; the mapping owner releases
The returned bytes view points into an owned mapping. Closing the original file descriptor is allowed; resetting the mapping invalidates all borrowed views.

In this task .bytes() is read-only and the mapping is MAP_PRIVATE. The public view exposes just the requested range, although the kernel manages whole pages. The reference keeps the map base and the view offset as separate fields so cleanup does not accidentally use the wrong address.

Visibility, copying and faults

MAP_PRIVATE uses copy-on-write for stores into a writable private mapping. Those private stores do not update the file. MAP_SHARED exposes shared file-backed pages; storage synchronization and inter-thread synchronization remain separate requirements. Private mapping alone is not a guarantee of an immutable snapshot if someone changes the underlying file.

Private and shared are different policies
Neither mapping mode is a synchronization protocol for multiple C++ threads; private mapping is also not an immutable snapshot guarantee.
Private and shared are different policies
Neither mapping mode is a synchronization protocol for multiple C++ threads; private mapping is also not an immutable snapshot guarantee.

A file descriptor can remain valid while the backing file is truncated. Access beyond the new backing range can deliver SIGBUS even though the virtual address was mapped earlier. The build explicitly requires the caller to keep file length stable; it does not deliberately fault the learner's process to demonstrate that case. A signal handler is not a substitute for a clear lifetime contract.

Before the fill, derive the map base, public-view pointer and unmap range for an unaligned slice crossing a page boundary. Tests compare bytes against a normal file read and keep a moved mapping alive after the file descriptor is closed. Continue with signals and events.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Follow mapping creation, page-backed file access and cleanup. The C++ build adds unaligned window arithmetic, move-only ownership, empty ranges and explicit lifetime rules.