Allocating memory to a process in C using the Linux System Call ABI involves understanding the memory layout of a process, the loading of ELF (Executable and Linkable Format) binaries into memory, and the mechanisms for memory allocation between user and kernel space. In this discussion, we'll explore these concepts and three separate methods for allocating memory: malloc, sbrk, and mmap.
1 Memory Layout of a Process
- A process's address space is divided into multiple segments, including text (code), data, heap, and stack segments.
- When a process is executed, the ELF binary is loaded into memory, with the text segment containing executable code, the data segment containing initialized data, the heap segment for dynamic memory allocation, and the stack segment for function call and local variables.
2 Loading of ELF Binaries
- When an ELF binary is executed, the kernel loads it into memory, typically at a base address specified by the linker.
- The text and data segments are mapped into memory as read-only and read-write sections, respectively.
- The heap and stack segments are initially small and may grow dynamically as needed during program execution.
3 Memory Mapping between User and Kernel Space
- The Linux kernel manages memory mapping between user and kernel space using virtual memory techniques.
- User space processes interact with the kernel through system calls, which allow them to request memory allocation, file operations, and other privileged operations.
- Memory protection mechanisms ensure that user space processes cannot access kernel memory directly and vice versa.
4 Methods for Memory Allocation
4.1 malloc:
- malloc is a standard C library function used for dynamic memory allocation from the heap segment.
- It requests memory from the heap managed by the memory allocator (e.g., glibc).
- Under the hood, malloc may use sbrk or mmap system calls to allocate memory from the kernel.
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
// Allocate memory for an array of 5 integers
int* ptr = (int*)malloc(5 * sizeof(int));
if (ptr == NULL) {
perror("malloc");
return 1;
}
// Initialize array elements
for (int i = 0; i < 5; i++) {
ptr[i] = i * 10;
}
// Print array elements
for (int i = 0; i < 5; i++) {
printf("%d ", ptr[i]);
}
// Free allocated memory
free(ptr);
return 0;
}
// Output
0 10 20 30 40
Explanation:
- This example demonstrates the use of malloc to dynamically allocate memory for an array of 5 integers.
- The malloc function allocates memory on the heap, and it returns a pointer to the allocated memory block.
- We initialize the array elements with values (
i * 10
) and print them. - Finally, we free the allocated memory using the free function to prevent memory leaks.
4.2 sbrk:
- sbrk is a system call used to adjust the end of the data segment (break) of a process's address space.
- It is typically used by memory allocators to request more memory from the kernel for the heap segment.
- sbrk increases or decreases the size of the heap by a specified number of bytes.
#include <stdio.h>
#include <unistd.h>
int main() {
// Allocate memory using sbrk
void* ptr = sbrk(5 * sizeof(int));
if (ptr == (void*)-1) {
perror("sbrk");
return 1;
}
// Assign values to the array elements
int* array = (int*)ptr;
for (int i = 0; i < 5; i++) {
array[i] = i + 1;
}
// Print the array elements
printf("Values using sbrk: ");
for (int i = 0; i < 5; i++) {
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
Example:
- This example uses sbrk to dynamically increase the program break by 5 integers (20 bytes).
- The newly allocated memory is treated as an array of integers, and values are assigned to it using a loop (
ptr[i] = i * 10;
). - The array elements are then printed to the console.
- Since sbrk directly adjusts the program break, we can access the allocated memory directly without needing to allocate/deallocate explicitly.
4.3 mmap:
- mmap is a system call used to map files or devices into memory or to create anonymous memory mappings.
- It can be used for dynamic memory allocation by creating anonymous memory mappings with MAP_ANONYMOUS flag.
- mmap provides more flexibility than sbrk for allocating memory, allowing finer control over memory mappings.
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
int main() {
// Allocate memory using mmap
void* ptr = mmap(NULL, 5 * sizeof(int), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
return 1;
}
// Assign values to the array elements
int* array = (int*)ptr;
for (int i = 0; i < 5; i++) {
array[i] = i + 1;
}
// Print the array elements
printf("Values using mmap: ");
for (int i = 0; i < 5; i++) {
printf("%d ", array[i]);
}
printf("\n");
// Deallocate memory
munmap(ptr, 5 * sizeof(int));
return 0;
}
// Output
0 10 20 30 40
Explanation:
- This example uses mmap to dynamically allocate memory for an array of 5 integers.
- Values are assigned to the array elements using a loop (
ptr[i] = i * 10;
). - The array elements are then printed to the console.
- Finally, the allocated memory is deallocated using munmap to release the memory back to the system.