Memory Management System Calls

In the previous parts, we covered system call basics, file I/O, process management and even built a mini-shell. Now, we turn our attention to memory management, a critical area for systems programming.

Memory management system calls allow programs to request, map and protect memory in a controlled way. This gives you the foundation for advanced topics like shared memory, memory-mapped files and performance optimization.

1. Memory Layout of a Process

A Linux process has several memory regions:

  • Text segment: contains the compiled program instructions
  • Data segment: contains global and static variables
  • Heap: dynamic memory allocated at runtime
  • Stack: local variables and function calls
  • Memory-mapped region: for files and shared memory

System calls primarily interact with the heap and memory-mapped regions.

2. Adjusting Heap Size: brk() and sbrk()

Traditionally, dynamic memory is managed using brk() and sbrk().

brk()

#include <unistd.h>

int brk(void *end_data_segment);
  • Sets the end of the data segment (heap).
  • Returns 0 on success, -1 on failure.

sbrk()

#include <unistd.h>

void *sbrk(intptr_t increment);
  • Moves the program break by increment bytes.
  • Returns the previous end of the heap.

Example:

#include <unistd.h>
#include <stdio.h>

int main() 
{
    void *heap_start = sbrk(0); 
    printf("Heap starts at %p\n", heap_start);

    sbrk(1024);
    printf("Heap increased by 1024 bytes\n");

    return 0;
}

Note: Most programs use malloc() instead of calling these directly. malloc() internally uses brk() or mmap().

3. Mapping Memory: mmap() and munmap()

mmap() maps files or anonymous memory into the process’s address space.

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • addr: suggested start address (usually NULL)
  • length: size of mapping
  • prot: memory protection (PROT_READ, PROT_WRITE)
  • flags: MAP_PRIVATE, MAP_SHARED, MAP_ANONYMOUS
  • fd: file descriptor (ignored if MAP_ANONYMOUS)
  • offset: offset in file

munmap() unmaps pages of memory.

#include <sys/mmap>

int munmap(void *addr, size_t len);
  • addr: remove process memory starting from this address
  • len: remove len bytes of memory starting from addr

Example of mapping a file into memory:

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() 
{
    int fd = open("example.txt", O_RDONLY);
    if (fd < 0) return 1;

    size_t size = 4096;
    char *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (data == MAP_FAILED) 
    {
        perror("mmap"); 
        return 1; 
    }

    printf("First byte: %c\n", data[0]);

    munmap(data, size);
    close(fd);
    return 0;
}

4. Controlling Memory Protections: mprotect()

mprotect() allows you to change the access permissions of a memory region.

#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);

Example: making a memory page read-only:

mprotect(addr, 4096, PROT_READ);

Returns 0 on success, -1 on failure.

5. Shared Memory via mmap()

Memory can be shared between processes using mmap() with MAP_SHARED and a file descriptor:

int fd = open("shared.bin", O_CREAT | O_RDWR, 0644);

ftruncate(fd, 4096); 

char *shared = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

Now, multiple processes that map this file can read and write the same memory.

6. Example: Modifying a File via Memory Mapping

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

int main() 
{
    int fd = open("file.txt", O_RDWR);
    size_t size = 4096;

    char *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (data == MAP_FAILED) 
    {
        perror("mmap"); 
        return 1; 
    }

    strcpy(data, "Hello mmap!"); 
    msync(data, size, MS_SYNC);  

    munmap(data, size);
    close(fd);

    return 0;
}

7. TL/DR

  • brk() and sbrk() adjust the heap size (low-level dynamic memory).
  • mmap() maps files or anonymous memory into a process’s address space.
  • munmap() unmaps pages of memory.
  • mprotect() changes memory protections dynamically.
  • Shared memory can be implemented via mmap() with MAP_SHARED.
  • Memory-mapped files allow direct modification of file contents in memory.

At this point, you understand the low-level memory management system calls in Linux and how they support dynamic memory, file mapping and shared memory.

Leave a Reply

Your email address will not be published. Required fields are marked *