Code

View on GitHub

Basic file mapping #

echo "I just love" > /tmp/poem.txt
echo "sharing memories" >> /tmp/poem.txt
echo "with other processes" >> /tmp/poem.txt
cat /tmp/poem.txt
make basic
./basic /tmp/poem.txt

Source: basic.c

Try experimenting:

  • is printf("%s") safe?
  • remove PROT_WRITE flag
  • remove PROT_READ flag
  • change to MAP_PRIVATE
  • try to read past end of the mapping
  • try to write past end of the mapping

Binary file mapping #

Memory mapping is extremely powerful for binary files. It allows you to treat the file contents directly as an array of C structures, bypassing serialization, read(), and write() entirely.

make binary
./binary /tmp/database.bin

Source: binary.c

cat /tmp/database.bin
xxd -c 40 /tmp/database.bin

IPC through file mappings #

Two or more processes can map the exact same file using MAP_SHARED. They will literally share the same physical memory pages. This is the fastest form of Inter-Process Communication (IPC), but it needs explicit synchronization.

Initialize an empty file (or zero it out):

Run single instance:

rm -f /tmp/counter.bin
make fsipc
./fsipc /tmp/counter.bin

Source: fsipc.c

Now run 3 instances in parallel:

rm -f /tmp/counter.bin
./fsipc /tmp/counter.bin &
./fsipc /tmp/counter.bin &
./fsipc /tmp/counter.bin &
wait

Notice the final value. Is it 3B?

# print integer value
od -An -t d4 /tmp/counter.bin

Try experimenting:

  • run 10 instances simultaneously in a bash loop. How low is the final value?
  • change mapping to MAP_PRIVATE

The address space #

The Linux kernel exposes the internal memory layout of every running process via the procfs virtual filesystem. By inspecting /proc/<pid>/maps, we can see exactly where the stack, heap, code, and our mmap() regions reside in the virtual address space.

make procmaps
./procmaps procmaps.c

Source: procmaps.c

cat /proc/$(pidof procmaps)/maps

Note how different parts of binary program are just mapped into the memory.

Anonymous shared mappings #

In this example, the parent and child share an anonymous memory mapping. The child blocks and waits for a specific signal (sigwait) before attempting to read the memory. The parent simulates some work, writes the data to the memory, and then sends SIGUSR1 to notify the child.

make anon
./anon procmaps.c

Source: anon.c

Experiment:

  • change to MAP_PRIVATE

POSIX shared memory #

make shm_basic
./shm_basic arbiter

Source: shm_basic.c

ls /dev/shm
xxd /dev/shm/hello_shm

After first pause:

watch -n0.1 xxd /dev/shm/hello_shm

Try experiment:

  • remove shm_unlink()
  • play with O_CREAT | O_EXCL

SHM Tug of war #

make shm_tug
./shm_tug arbiter

Source: shm_tug.c

Run in separate terminal:

./shm_tug left & ./shm_tug right &
wait

Note this program is invalid due to lack of synchronization.

Try experiment:

  • compile with optimizations (-O2)