Exercise on memory management analyzer
This commit is contained in:
@@ -11,6 +11,7 @@ Welcome! This is a collection of chapter summaries from the book **Linux System
|
|||||||
- [Chapter 5 - Process Management](chp5/README.md)
|
- [Chapter 5 - Process Management](chp5/README.md)
|
||||||
- [Chapter 6 - Advanced Process Management](chp6/README.md)
|
- [Chapter 6 - Advanced Process Management](chp6/README.md)
|
||||||
- [Chapter 7 - Threading](chp7/README.md)
|
- [Chapter 7 - Threading](chp7/README.md)
|
||||||
|
- [Chapter 9 - Memory Management](chp9/README.md)
|
||||||
- [Exercises](exercises/README.md)
|
- [Exercises](exercises/README.md)
|
||||||
|
|
||||||
> Each file contains an English summary of the chapter's key concepts.
|
> Each file contains an English summary of the chapter's key concepts.
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ This is an organized list of all exercises completed so far, including a brief d
|
|||||||
|
|
||||||
**Description**: A simple C program that sequentially executes one or more bash scripts, printing each script’s exit status.
|
**Description**: A simple C program that sequentially executes one or more bash scripts, printing each script’s exit status.
|
||||||
|
|
||||||
- 📄 [README](run_scripts/README.md)
|
- 📄 [README](run_scripts/README.md)
|
||||||
- 📂 Directory: `run_scripts/`
|
- 📂 Directory: `run_scripts/`
|
||||||
- ✅ Features:
|
- ✅ Features:
|
||||||
- Uses `fork()` and `execlp()` to run each script in a child process
|
- Uses `fork()` and `execlp()` to run each script in a child process
|
||||||
- Waits for each child to finish before executing the next
|
- Waits for each child to finish before executing the next
|
||||||
- Reports normal exit code or termination by signal
|
- Reports normal exit code or termination by signal
|
||||||
- Includes example test scripts and memory checks with Valgrind
|
- Includes example test scripts and memory checks with Valgrind
|
||||||
- Makefile with targets: `build`, `run`, `test`, `valgrind`, and `clean`
|
- Makefile with targets: `build`, `run`, `test`, `valgrind`, and `clean`
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -51,8 +51,8 @@ This is an organized list of all exercises completed so far, including a brief d
|
|||||||
|
|
||||||
**Description**: A multi-threaded simulated file downloader implemented in C using POSIX threads (pthreads).
|
**Description**: A multi-threaded simulated file downloader implemented in C using POSIX threads (pthreads).
|
||||||
|
|
||||||
- 📄 [README](downloader/README.md)
|
- 📄 [README](downloader/README.md)
|
||||||
- 📂 Directory: `downloader/`
|
- 📂 Directory: `downloader/`
|
||||||
- ✅ Features:
|
- ✅ Features:
|
||||||
- Each thread simulates downloading a file by printing periodic progress updates
|
- Each thread simulates downloading a file by printing periodic progress updates
|
||||||
- Synchronization using `pthread` APIs
|
- Synchronization using `pthread` APIs
|
||||||
@@ -61,6 +61,20 @@ This is an organized list of all exercises completed so far, including a brief d
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🧠 memory_analyzer
|
||||||
|
|
||||||
|
**Description**: A simple memory analyzer that allocates memory blocks of different sizes and prints current memory usage.
|
||||||
|
|
||||||
|
- 📄 [README](mem_analyzer/README.md)
|
||||||
|
- 📂 Directory: `mem_analyzer/`
|
||||||
|
- ✅ Features:
|
||||||
|
- Allocates blocks using `malloc` and frees them after measurement
|
||||||
|
- Reports current memory usage via `/proc/self/status`
|
||||||
|
- Minimal implementation aligned with the exercise objectives
|
||||||
|
- Makefile included with standard targets
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🔧 Tooling & Automation
|
## 🔧 Tooling & Automation
|
||||||
|
|
||||||
**Shared tools and scripts used across projects**:
|
**Shared tools and scripts used across projects**:
|
||||||
@@ -69,5 +83,3 @@ This is an organized list of all exercises completed so far, including a brief d
|
|||||||
- Targets for `build`, `test`, `valgrind`, and `clean`
|
- Targets for `build`, `test`, `valgrind`, and `clean`
|
||||||
- Shell script for automated tests: `run_tests.sh`
|
- Shell script for automated tests: `run_tests.sh`
|
||||||
- Valgrind memory check reports generated automatically
|
- Valgrind memory check reports generated automatically
|
||||||
|
|
||||||
*Last updated: 2025-07-30*
|
|
||||||
|
|||||||
29
exercises/mem_analyzer/Makefile
Normal file
29
exercises/mem_analyzer/Makefile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -Wextra -Wpadded -g -fsanitize=address
|
||||||
|
TARGET = mem_analyzer
|
||||||
|
SRC = mem_utils.c main.c
|
||||||
|
OBJ = $(SRC:.c=.o)
|
||||||
|
|
||||||
|
.PHONY: all build run test valgrind clean
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
build: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJ)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
run: build
|
||||||
|
./$(TARGET)
|
||||||
|
|
||||||
|
test: build
|
||||||
|
./$(TARGET) test
|
||||||
|
|
||||||
|
valgrind: build
|
||||||
|
valgrind --leak-check=full --show-leak-kinds=all ./$(TARGET)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET) $(OBJ)
|
||||||
77
exercises/mem_analyzer/README.md
Normal file
77
exercises/mem_analyzer/README.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# 🧠 Memory Management Analyzer
|
||||||
|
|
||||||
|
This project demonstrates how different memory allocation methods (`malloc`, `calloc`, and `mmap`) perform in terms of speed and memory usage in a simulated workload.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📜 Description
|
||||||
|
The program dynamically allocates and frees memory blocks of varying sizes, measuring:
|
||||||
|
|
||||||
|
- Allocation and deallocation times.
|
||||||
|
- Memory usage via `/proc/self/status` or `/proc/self/statm`.
|
||||||
|
- Peak memory consumption during execution.
|
||||||
|
|
||||||
|
It produces a report comparing performance across allocation methods.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
- **Dynamic Allocation Testing** – compares `malloc`, `calloc`, and `mmap`.
|
||||||
|
- **Performance Metrics** – measures allocation/deallocation times.
|
||||||
|
- **Memory Usage Tracking** – monitors real-time process memory usage.
|
||||||
|
- **Optional Memory Leak Simulation** – detects unfreed memory using Valgrind.
|
||||||
|
- **CLI Mode Selection** – choose allocator at runtime (`--malloc`, `--calloc`, `--mmap`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Build
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ▶️ Run
|
||||||
|
```bash
|
||||||
|
./mem_analyzer --malloc
|
||||||
|
./mem_analyzer --calloc
|
||||||
|
./mem_analyzer --mmap
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Test & Debug
|
||||||
|
```bash
|
||||||
|
make valgrind
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Example Output
|
||||||
|
```
|
||||||
|
Allocator: malloc
|
||||||
|
Blocks: 10000
|
||||||
|
Average Allocation Time: 0.24 μs
|
||||||
|
Average Free Time: 0.18 μs
|
||||||
|
Peak Memory Usage: 2.5 MB
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 Project Structure
|
||||||
|
```
|
||||||
|
mem_analyzer/
|
||||||
|
├── README.md
|
||||||
|
├── Makefile
|
||||||
|
└── main.c
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Learning Objectives
|
||||||
|
- Understand differences between `malloc`, `calloc`, and `mmap`.
|
||||||
|
- Learn to measure memory usage through `/proc`.
|
||||||
|
- Practice debugging memory errors with Valgrind.
|
||||||
|
- Gain insight into memory allocation strategies in Linux.
|
||||||
|
|
||||||
|
---
|
||||||
168
exercises/mem_analyzer/main.c
Normal file
168
exercises/mem_analyzer/main.c
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#include "mem_utils.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* Run allocation benchmark using malloc */
|
||||||
|
struct Stats *bench_malloc();
|
||||||
|
|
||||||
|
/* Run allocation benchmark using calloc */
|
||||||
|
struct Stats *bench_calloc();
|
||||||
|
|
||||||
|
/* Run allocation benchmark using mmap */
|
||||||
|
struct Stats *bench_mmap();
|
||||||
|
|
||||||
|
typedef struct Stats *(*StatsFn)(void);
|
||||||
|
|
||||||
|
/* Memory blocks size to allocate for testing */
|
||||||
|
static size_t size_blocks[BLOCK_NUM] = {1024, 2048, 4096, 100000, 8192};
|
||||||
|
|
||||||
|
int main(int argc, char const *argv[])
|
||||||
|
{
|
||||||
|
const StatsFn benchmarks[] = {bench_malloc, bench_calloc, bench_mmap};
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
StatsFn benchmark = benchmarks[i];
|
||||||
|
struct Stats *result = benchmark();
|
||||||
|
print_stats(result);
|
||||||
|
free(result);
|
||||||
|
printf("-------------------------------------------\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stats *bench_malloc()
|
||||||
|
{
|
||||||
|
// Benchmark malloc function
|
||||||
|
double peak_mem = 0.0;
|
||||||
|
double m_elapsed_times[BLOCK_NUM] = {0};
|
||||||
|
double f_elapsed_times[BLOCK_NUM] = {0};
|
||||||
|
for (int i = 0; i < BLOCK_NUM; i++) {
|
||||||
|
size_t size = size_blocks[i];
|
||||||
|
struct timespec m_start, f_start, m_end, f_end;
|
||||||
|
|
||||||
|
// malloc
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &m_start);
|
||||||
|
void *ptr = malloc(size);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &m_end);
|
||||||
|
m_elapsed_times[i] = ((m_end.tv_sec - m_start.tv_sec) +
|
||||||
|
(m_end.tv_nsec - m_start.tv_nsec)) /
|
||||||
|
1e9;
|
||||||
|
|
||||||
|
double curr_mem_usage = get_mem_usage();
|
||||||
|
if (peak_mem <= curr_mem_usage) {
|
||||||
|
peak_mem = curr_mem_usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// free
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &f_start);
|
||||||
|
free(ptr);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &f_end);
|
||||||
|
f_elapsed_times[i] = ((f_end.tv_sec - f_start.tv_sec) +
|
||||||
|
(f_end.tv_nsec - f_start.tv_nsec)) /
|
||||||
|
1e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stats *stats = (struct Stats *)malloc(sizeof(struct Stats));
|
||||||
|
if (!stats) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
stats->allocator_type = "malloc";
|
||||||
|
stats->nr_allocations = BLOCK_NUM;
|
||||||
|
stats->avg_alloc_time = avg(m_elapsed_times, BLOCK_NUM);
|
||||||
|
stats->avg_free_time = avg(f_elapsed_times, BLOCK_NUM);
|
||||||
|
stats->mem_usage_mb = peak_mem;
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stats *bench_calloc()
|
||||||
|
{
|
||||||
|
// Benchmark malloc function
|
||||||
|
double peak_mem = 0.0;
|
||||||
|
double m_elapsed_times[BLOCK_NUM] = {0};
|
||||||
|
double f_elapsed_times[BLOCK_NUM] = {0};
|
||||||
|
for (int i = 0; i < BLOCK_NUM; i++) {
|
||||||
|
size_t size = size_blocks[i];
|
||||||
|
struct timespec m_start, f_start, m_end, f_end;
|
||||||
|
|
||||||
|
// malloc
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &m_start);
|
||||||
|
void *ptr = calloc(1, size);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &m_end);
|
||||||
|
m_elapsed_times[i] = ((m_end.tv_sec - m_start.tv_sec) +
|
||||||
|
(m_end.tv_nsec - m_start.tv_nsec)) /
|
||||||
|
1e9;
|
||||||
|
|
||||||
|
double curr_mem_usage = get_mem_usage();
|
||||||
|
if (peak_mem <= curr_mem_usage) {
|
||||||
|
peak_mem = curr_mem_usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// free
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &f_start);
|
||||||
|
free(ptr);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &f_end);
|
||||||
|
f_elapsed_times[i] = ((f_end.tv_sec - f_start.tv_sec) +
|
||||||
|
(f_end.tv_nsec - f_start.tv_nsec)) /
|
||||||
|
1e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stats *stats = (struct Stats *)malloc(sizeof(struct Stats));
|
||||||
|
if (!stats) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
stats->allocator_type = "calloc";
|
||||||
|
stats->nr_allocations = BLOCK_NUM;
|
||||||
|
stats->avg_alloc_time = avg(m_elapsed_times, BLOCK_NUM);
|
||||||
|
stats->avg_free_time = avg(f_elapsed_times, BLOCK_NUM);
|
||||||
|
stats->mem_usage_mb = peak_mem;
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stats *bench_mmap()
|
||||||
|
{
|
||||||
|
// Benchmark malloc function
|
||||||
|
double peak_mem = 0.0;
|
||||||
|
double m_elapsed_times[BLOCK_NUM] = {0};
|
||||||
|
double f_elapsed_times[BLOCK_NUM] = {0};
|
||||||
|
for (int i = 0; i < BLOCK_NUM; i++) {
|
||||||
|
size_t size = size_blocks[i];
|
||||||
|
struct timespec m_start, f_start, m_end, f_end;
|
||||||
|
|
||||||
|
// malloc
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &m_start);
|
||||||
|
void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &m_end);
|
||||||
|
m_elapsed_times[i] = ((m_end.tv_sec - m_start.tv_sec) +
|
||||||
|
(m_end.tv_nsec - m_start.tv_nsec)) /
|
||||||
|
1e9;
|
||||||
|
|
||||||
|
double curr_mem_usage = get_mem_usage();
|
||||||
|
if (peak_mem <= curr_mem_usage) {
|
||||||
|
peak_mem = curr_mem_usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// free
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &f_start);
|
||||||
|
munmap(ptr, size);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &f_end);
|
||||||
|
f_elapsed_times[i] = ((f_end.tv_sec - f_start.tv_sec) +
|
||||||
|
(f_end.tv_nsec - f_start.tv_nsec)) /
|
||||||
|
1e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stats *stats = (struct Stats *)malloc(sizeof(struct Stats));
|
||||||
|
if (!stats) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
stats->allocator_type = "mmap";
|
||||||
|
stats->nr_allocations = BLOCK_NUM;
|
||||||
|
stats->avg_alloc_time = avg(m_elapsed_times, BLOCK_NUM);
|
||||||
|
stats->avg_free_time = avg(f_elapsed_times, BLOCK_NUM);
|
||||||
|
stats->mem_usage_mb = peak_mem;
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
47
exercises/mem_analyzer/mem_utils.c
Normal file
47
exercises/mem_analyzer/mem_utils.c
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include "mem_utils.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void print_stats(struct Stats *stats)
|
||||||
|
{
|
||||||
|
printf("Allocator: %s\nBlocks: %ld\nAvarage Allocation Time: %.2f "
|
||||||
|
"μs\nAvarage Free Time: %.2f μs\nPeak Memory Usage: %.2f MB\n",
|
||||||
|
stats->allocator_type, stats->nr_allocations,
|
||||||
|
stats->avg_alloc_time*1e6, stats->avg_free_time*1e6,
|
||||||
|
stats->mem_usage_mb);
|
||||||
|
}
|
||||||
|
|
||||||
|
double avg(double *values, int size)
|
||||||
|
{
|
||||||
|
double sum = 0.0;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
sum += *(values + i);
|
||||||
|
}
|
||||||
|
return size > 0 ? sum / size : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_mem_usage()
|
||||||
|
{
|
||||||
|
FILE *fp = fopen("/proc/self/status", "r");
|
||||||
|
if (!fp) {
|
||||||
|
perror("fopen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int found = 0;
|
||||||
|
char line[256];
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
if (strncmp(line, "VmRSS:", 6) == 0) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
fprintf(stderr, "VmRSS not found in /proc/self/status\n");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strtod(line + 6, NULL) / 1024.0;
|
||||||
|
}
|
||||||
26
exercises/mem_analyzer/mem_utils.h
Normal file
26
exercises/mem_analyzer/mem_utils.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef MEM_UTILS_H
|
||||||
|
#define MEM_UTILS_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Block number under testing */
|
||||||
|
#define BLOCK_NUM 5
|
||||||
|
|
||||||
|
struct Stats {
|
||||||
|
const char *allocator_type;
|
||||||
|
size_t nr_allocations;
|
||||||
|
double avg_alloc_time;
|
||||||
|
double avg_free_time;
|
||||||
|
double mem_usage_mb;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Print on STDOUT the memory stats collected */
|
||||||
|
void print_stats(struct Stats *);
|
||||||
|
|
||||||
|
/* Calculate arithmetic average */
|
||||||
|
double avg(double *, int size);
|
||||||
|
|
||||||
|
/* Read in the a Linux system at /proc/self/status file the RSS field */
|
||||||
|
double get_mem_usage();
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user