1
0

feat(lab04): add exercices 2 cgroups

This commit is contained in:
2026-05-26 12:59:40 +00:00
parent 820cbe6d86
commit 4179e73ba9
6 changed files with 166 additions and 7 deletions

View File

@@ -48,10 +48,7 @@
//
#include "lab03-silly_led/main.typ"
#lorem(150)
#lorem(50)
#include "lab04-multiprocessing/main.typ"
//--------------------------------------

View File

@@ -1,3 +1,93 @@
#import "/doc/metadata.typ": *
= Multiprocessing
== CGroups
The goal of this part is to understand how to use cgroups to limit the resources of a process. We will initially focus on memory, but cgroups can also be used to limit CPU, I/O, and other ressources.
For limit the memory usage of a process, we cans use the `memory` subsystem of cgroups. We use cgroup v1 with our Nanopi.
We must first mount a temporary filesystem for cgroups:
```bash
|> mount -t tmpfs none /sys/fs/cgroup
```
We can the create a directory for the memory cgroup, mount the cgroup filesystem with memory, and create a subdirectory for our cgroup:
```bash
# Create a directory for the memory cgroup
|> mkdir /sys/fs/cgroup/memory
# Mount the cgroup filesystem with memory
|> mount -t cgroup -o memory cgroup /sys/fs/cgroup/memory
# Create a subdirectory for the memory cgroup
|> mkdir /sys/fs/cgroup/memory/0
```
We can then add the current process to this memory cgroups and set a memory limit of 20 MiB:
```bash
# Add the current process to the memory cgroup
|> echo $$ > /sys/fs/cgroup/memory/0/tasks
# Set the memory limit to 20 MiB
|> echo 20M > /sys/fs/cgroup/memory/0/memory.limit_in_bytes
```
We can then run our test program that allocates memory in a loop and see what happens when we exceed the memory limit.
```c
for (i = 0; i < NUM_BLOCKS; i++) {
// Allocate a block of memory
blocks[i] = malloc(BLOCK_SIZE);
// [...]
// check if failed and error, clean and exit
// Touch the memory to ensure it's actually allocated
memset(blocks[i], 0, BLOCK_SIZE);
}
```
We can use the `cgroups.sh` script in `04-multiprocessing` to set up the cgroup and run the test program, but we need to run with the actual context, so we need to execute the script with `.`:
```bash
|> just cgroups # Build the test program
|> . cgroups.sh # Run the script in the current shell
|> ./cgroups # Run the test program that allocates memory in a loop
```
=== What is the behavior of the command `echo $$ > ...` on cgroups?
The `$$` represent the current process ID (PID). When we execute the command `echo $$ > /sys/fs/cgroup/memory/0/tasks`, we are writing the PID of the current process into the `tasks` file of the specified cgroup. This action effectively assigns the process to that cgroup, meaning that it will now be subject to the resource limits and policies defined for that cgroup.
=== What is the behavior of the memory subsystem when the memory quota is exhausted? Can we modify it? If yes, how?
For this nanopi, we use cgroup v1, so the relevant file is `memory.limit_in_bytes`. When a process within a cgroup exceeds the memory limit defined by `memory.limit_in_bytes`, the Linux kernel will attempt to reclaim memory. If it cannot reclaim enough memory, it will invoke the Out Of Memory (OOM) killer to terminate processes within that cgroup to free up memory.
It's possible to modify this behavior in several ways:
+ Use "Soft Limits" (Specific to cgroup v1)
In addition to a hard limit (`memory.limit_in_bytes`), you can set a soft limit (`memory.soft_limit_in_bytes`).
*Behavior:* The kernel will not kill the process if the soft limit is exceeded, unless the entire system is low on global memory. If global memory is low, the kernel will start reclaiming memory from groups that exceed their soft limit.
+ Adjust the OOM Killer Priority Score
If we specify an OOM score adjustement for the process. By modifying the file `/proc/[PID]/oom_score_adj` with the value `-1000`, we can make the process almost "immune" to the OOM Killer.
=== How to watch the memory usage?
We can monitor the memory usage of a cgroup by reading it directly from the file in the specific cgroups:
```bash
# Current memory usage in bytes
|> cat /sys/fs/cgroup/memory/0/memory.usage_in_bytes
212992
# Maximum memory usage in bytes
|> cat /sys/fs/cgroup/memory/0/memory.max_usage_in_bytes
20971520
```

View File

@@ -1,5 +1,8 @@
EXE=multiprocessing
SRCS=process.c
cgroups:
$(MAKE) EXE=cgroups SRCS=cgroups.c all
# Include the standard application Makefile for the CSEL1 labs
include ../appl.mk

View File

@@ -0,0 +1,44 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define NUM_BLOCKS 50
#define BLOCK_SIZE (1024 * 1024) // 1 MiB
// 2^20 = 1048576
// 1024*1024 = 1048576
int main() {
void *blocks[NUM_BLOCKS];
int i;
printf("Allocate %d blocks of 1 MiB\n", NUM_BLOCKS);
for (i = 0; i < NUM_BLOCKS; i++) {
blocks[i] = malloc(BLOCK_SIZE);
if (blocks[i] == NULL) {
fprintf(stderr, "Error: Allocation not possible for block %d (Limit reached!)\n", i);
for (int j = 0; j < i; j++) {
free(blocks[j]);
}
return 1;
}
memset(blocks[i], 0, BLOCK_SIZE); // Touch the memory to ensure it's actually allocated
printf("Block %d allocated and initialized successfully.\n", i);
usleep(100000); // 100ms
}
printf("Success: All blocks have been allocated.\n");
// Release the memory at the end
for (i = 0; i < NUM_BLOCKS; i++) {
free(blocks[i]);
}
return 0;
}

View File

@@ -0,0 +1,15 @@
#!/bin/sh
# mount -t tmpfs none /sys/fs/cgroup
# mkdir /sys/fs/cgroup/set
# mount -t cgroup -o memory,cpu,cpuset cgroups /sys/fs/cgroup/set
mount -t tmpfs none /sys/fs/cgroup # Mount a temporary filesystem to /sys/fs/cgroup
# Memory
mkdir /sys/fs/cgroup/memory # Create a directory for the memory cgroup
mount -t cgroup -o memory cgroup /sys/fs/cgroup/memory # Mount the cgroup filesystem with memory
mkdir /sys/fs/cgroup/memory/0 # Create a subdirectory for the memory cgroup
echo $$ > /sys/fs/cgroup/memory/0/tasks # Add the current process to the memory cgroup
echo 20M > /sys/fs/cgroup/memory/0/memory.limit_in_bytes # Set the memory limit to 20 MiB

View File

@@ -1,7 +1,17 @@
build:
@default:
just --list
@build:
make
clean:
@process:
make
@cgroups:
make cgroups
@clean:
rm -rf build
rm -rf .obj
rm multiprocessing
rm -f -- multiprocessing
rm -f -- cgroups