feat(lab04): add cgroups CPU
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -60,6 +60,7 @@ build
|
||||
src/03-led-controller/led-controller
|
||||
src/04-multiprocessing/multiprocessing
|
||||
src/04-multiprocessing/cgroups
|
||||
src/04-multiprocessing/max-cpu
|
||||
src/05-optimization/ex01/basic
|
||||
src/05-optimization/ex01/optimized
|
||||
src/05-optimization/ex02/optimized
|
||||
|
||||
@@ -98,13 +98,13 @@ SIGINT received
|
||||
The @multiprocessus shows the PID and the core of the processus and they can be compared to the output of the executable before.
|
||||
The child processus has the PID 273 and the core 0. The parent processus has th PID 274 and the core 1.
|
||||
|
||||
== CGroups
|
||||
== CGroups memory
|
||||
|
||||
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.
|
||||
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:
|
||||
We must first mount a temporary filesystem for cgroups:
|
||||
```bash
|
||||
|> mount -t tmpfs none /sys/fs/cgroup
|
||||
```
|
||||
@@ -138,8 +138,8 @@ We can then run our test program that allocates memory in a loop and see what ha
|
||||
for (i = 0; i < NUM_BLOCKS; i++) {
|
||||
|
||||
// Allocate a block of memory
|
||||
blocks[i] = malloc(BLOCK_SIZE);
|
||||
|
||||
blocks[i] = malloc(BLOCK_SIZE);
|
||||
|
||||
// [...]
|
||||
// check if failed and error, clean and exit
|
||||
|
||||
@@ -176,7 +176,7 @@ It's possible to modify this behavior in several ways:
|
||||
|
||||
=== 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:
|
||||
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
|
||||
@@ -187,3 +187,87 @@ We can monitor the memory usage of a cgroup by reading it directly from the file
|
||||
|> cat /sys/fs/cgroup/memory/0/memory.max_usage_in_bytes
|
||||
20971520
|
||||
```
|
||||
|
||||
== CGroups CPU
|
||||
To check this part, we need a tiny program that consumes CPU with at least two process.
|
||||
The following program creates a child process that performs CPU intensive work, while the parent process also performs CPU intensive work. We can then use cgroups to limit the CPU usage of one of the processes and observe the effect.
|
||||
```c
|
||||
int main() {
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
cpu_intensive_work("Child process");
|
||||
exit(0);
|
||||
} else {
|
||||
cpu_intensive_work("Parent process");
|
||||
wait(NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Based on previous exercice, we should already have mounted the cgroup filesystem.
|
||||
```bash
|
||||
|> mount -t tmpfs none /sys/fs/cgroup
|
||||
```
|
||||
|
||||
We can then create and mount the cgroup filesystem for the `cpuset` subsystem
|
||||
```bash
|
||||
# Create a directory for the cpuset cgroup
|
||||
|> mkdir /sys/fs/cgroup/cpuset
|
||||
|
||||
# Mount the cgroup filesystem with cpuset
|
||||
|> mount -t cgroup -o cpu,cpuset cpuset /sys/fs/cgroup/cpuset
|
||||
```
|
||||
|
||||
Now we had the prerequirements, we can create 2 groupes. One for each of our running programme. With the following command, we attribute on ore more CPU to each group (`cpuset.cpus`). I'm not sure about the `cpuset.mems` file, but it seems to be related to memory nodes. It's definetly a topic that should be explored more in depth, but for now, we set to `0` as specified in the lab instructions.
|
||||
|
||||
```bash
|
||||
# Create and allocate CPU for programme "low"
|
||||
|> mkdir /sys/fs/cgroup/cpuset/low
|
||||
|> echo 1 > /sys/fs/cgroup/cpuset/low/cpuset.cpus
|
||||
|> echo 0 > /sys/fs/cgroup/cpuset/low/cpuset.mems
|
||||
|
||||
# Create and allocate CPU for programme "high"
|
||||
|> mkdir /sys/fs/cgroup/cpuset/high
|
||||
|> echo 2,3 > /sys/fs/cgroup/cpuset/high/cpuset.cpus
|
||||
|> echo 0 > /sys/fs/cgroup/cpuset/high/cpuset.mems
|
||||
```
|
||||
|
||||
We can then open 2 shells and run the test program in each of them, while adding the programme to the corresponding cgroup:
|
||||
```bash
|
||||
# In the first shell, add it on the "low" cgroup and run the test program
|
||||
|> . ./max-cpu.sh low
|
||||
|
||||
# In the second shell, add it on the "high" cgroup and run the test program
|
||||
|> . ./max-cpu.sh high
|
||||
```
|
||||
|
||||
We see on @max-cpu that as expected, both process in program _low_ is limited to CPU 1, while the programm _high_ is using CPU 2 and 3, one for each process.
|
||||
|
||||
#figure(
|
||||
image("max-cpu.png"),
|
||||
caption: [CPU usage of the two programmes with dedicated resources]
|
||||
)<max-cpu>
|
||||
|
||||
To share resources at 75% and 25%, we can use the `cpu.shares` file in the `cpu` cgroup. We attribute a value 3 time high for the _high_ group than for the _low_ group.
|
||||
|
||||
```bash
|
||||
|> echo 75 > /sys/fs/cgroup/cpu/high/cpu.shares
|
||||
|> echo 25 > /sys/fs/cgroup/cpu/low/cpu.shares
|
||||
```
|
||||
|
||||
Then running the test program in each shell, we see on @shared-cpu that the _high_ process is limited to 75% of the CPU, while the _low_ process is limited to 25%.
|
||||
```bash
|
||||
# In the first shell, add it on the "low" cgroup and run the test program
|
||||
|> . ./shared-cpu.sh low
|
||||
|
||||
# In the second shell, add it on the "high" cgroup and run the test program
|
||||
|> . ./shared-cpu.sh high
|
||||
```
|
||||
|
||||
|
||||
#figure(
|
||||
image("shared-cpu.png"),
|
||||
caption: [CPU usage of the two programmes with shared resources]
|
||||
)<shared-cpu>
|
||||
BIN
doc/lab04-multiprocessing/max-cpu.png
Normal file
BIN
doc/lab04-multiprocessing/max-cpu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
BIN
doc/lab04-multiprocessing/shared-cpu.png
Normal file
BIN
doc/lab04-multiprocessing/shared-cpu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
@@ -1,11 +1,14 @@
|
||||
|
||||
|
||||
|
||||
cgroups:
|
||||
$(MAKE) EXE=cgroups SRCS=cgroups.c all
|
||||
|
||||
process:
|
||||
$(MAKE) EXE=multiprocessing SRCS=process.c all
|
||||
|
||||
cgroups:
|
||||
$(MAKE) EXE=cgroups SRCS=cgroups.c all
|
||||
|
||||
|
||||
max-cpu:
|
||||
$(MAKE) EXE=max-cpu SRCS=max-cpu.c all
|
||||
|
||||
# Include the standard application Makefile for the CSEL1 labs
|
||||
include ../appl.mk
|
||||
|
||||
@@ -10,8 +10,12 @@
|
||||
@cgroups:
|
||||
make cgroups
|
||||
|
||||
@max-cpu:
|
||||
make max-cpu
|
||||
|
||||
@clean:
|
||||
rm -rf build
|
||||
rm -rf .obj
|
||||
rm -f -- multiprocessing
|
||||
rm -f -- cgroups
|
||||
rm -f -- max-cpu
|
||||
|
||||
70
src/04-multiprocessing/max-cpu.c
Normal file
70
src/04-multiprocessing/max-cpu.c
Normal file
@@ -0,0 +1,70 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
|
||||
int fork(void);
|
||||
|
||||
|
||||
volatile unsigned long counter = 0;
|
||||
void cpu_intensive_work(const char *process_name) {
|
||||
printf("%s (PID: %d) starting CPU-intensive work on CPU %d\n", process_name, getpid(), sched_getcpu());
|
||||
|
||||
while (1) {
|
||||
counter++;
|
||||
counter = (counter * counter + counter) % (counter * 1023);
|
||||
}
|
||||
}
|
||||
|
||||
void print_usage(const char *prog) {
|
||||
fprintf(stderr, "Usage: %s [--single] [--dual]\n", prog);
|
||||
fprintf(stderr, " --single Run single process (default)\n");
|
||||
fprintf(stderr, " --dual Run two processes\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int use_dual = 0;
|
||||
|
||||
if (argc > 1) {
|
||||
if (strcmp(argv[1], "--dual") == 0) {
|
||||
use_dual = 1;
|
||||
} else if (strcmp(argv[1], "--single") == 0) {
|
||||
use_dual = 0;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[1]);
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_dual) {
|
||||
// Dual-process mode
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
cpu_intensive_work("Child process");
|
||||
exit(0);
|
||||
} else if (pid > 0) {
|
||||
cpu_intensive_work("Parent process");
|
||||
wait(NULL);
|
||||
return 0;
|
||||
} else {
|
||||
perror("fork failed");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Single-process mode
|
||||
cpu_intensive_work("Process");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
57
src/04-multiprocessing/max-cpu.sh
Executable file
57
src/04-multiprocessing/max-cpu.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 {init|high|low}"
|
||||
echo " init - Initialize cgroup filesystem and cpuset groups"
|
||||
echo " high - Run ./max-cpu --dual in the 'high' cgroup (CPUs 2,3)"
|
||||
echo " low - Run ./max-cpu --dual in the 'low' cgroup (CPU 1)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
init)
|
||||
echo "Initializing cgroup filesystem..."
|
||||
|
||||
# Mount tmpfs for cgroup
|
||||
mount -t tmpfs none /sys/fs/cgroup 2>/dev/null
|
||||
|
||||
# Create and mount cpuset cgroup
|
||||
mkdir -p /sys/fs/cgroup/cpuset
|
||||
mount -t cgroup -o cpu,cpuset cpuset /sys/fs/cgroup/cpuset 2>/dev/null
|
||||
|
||||
# Create "low" cgroup and allocate CPU 1
|
||||
mkdir -p /sys/fs/cgroup/cpuset/low
|
||||
echo 1 > /sys/fs/cgroup/cpuset/low/cpuset.cpus
|
||||
echo 0 > /sys/fs/cgroup/cpuset/low/cpuset.mems
|
||||
|
||||
# Create "high" cgroup and allocate CPUs 2,3
|
||||
mkdir -p /sys/fs/cgroup/cpuset/high
|
||||
echo 2,3 > /sys/fs/cgroup/cpuset/high/cpuset.cpus
|
||||
echo 0 > /sys/fs/cgroup/cpuset/high/cpuset.mems
|
||||
|
||||
echo "Cgroup initialization complete."
|
||||
echo " - low group: CPU 1"
|
||||
echo " - high group: CPUs 2,3"
|
||||
;;
|
||||
|
||||
high)
|
||||
echo "Running ./max-cpu --dual in 'high' cgroup (CPUs 2,3)..."
|
||||
echo $$ > /sys/fs/cgroup/cpuset/high/tasks
|
||||
./max-cpu --dual
|
||||
;;
|
||||
|
||||
low)
|
||||
echo "Running ./max-cpu --dual in 'low' cgroup (CPU 1)..."
|
||||
echo $$ > /sys/fs/cgroup/cpuset/low/tasks
|
||||
./max-cpu --dual
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown command: $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
96
src/04-multiprocessing/shared-cpu.sh
Executable file
96
src/04-multiprocessing/shared-cpu.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/bin/sh
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 {init|high|low}"
|
||||
echo " ./shared-cpu.sh init - Initialize cgroup filesystem with cpu.shares groups"
|
||||
echo " . ./shared-cpu.sh high - Run ./max-cpu --single in 'high' cgroup (75% CPU share on CPU 0)"
|
||||
echo " . ./shared-cpu.sh low - Run ./max-cpu --single in 'low' cgroup (25% CPU share on CPU 0)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
# Helper to check if a directory is already mounted
|
||||
is_mounted() {
|
||||
mount | grep -q " $1 "
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
init)
|
||||
echo "Initializing cgroup filesystem for CPU shares..."
|
||||
|
||||
# Mount tmpfs for cgroup if not already mounted
|
||||
if ! is_mounted /sys/fs/cgroup; then
|
||||
echo "Mounting /sys/fs/cgroup..."
|
||||
mount -t tmpfs none /sys/fs/cgroup || {
|
||||
echo "ERROR: Failed to mount /sys/fs/cgroup"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
echo "/sys/fs/cgroup is already mounted."
|
||||
fi
|
||||
|
||||
# Create and mount cpuset cgroup if not already mounted
|
||||
mkdir -p /sys/fs/cgroup/cpuset
|
||||
if ! is_mounted /sys/fs/cgroup/cpuset; then
|
||||
echo "Mounting cpuset/cpu cgroup..."
|
||||
mount -t cgroup -o cpu,cpuset cpuset /sys/fs/cgroup/cpuset || {
|
||||
echo "ERROR: Failed to mount cpuset cgroup! Perhaps 'cpu' and 'cpuset' are already mounted separately elsewhere?"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
echo "/sys/fs/cgroup/cpuset is already mounted."
|
||||
fi
|
||||
|
||||
# Create "low" cgroup with 25% share
|
||||
echo "Configuring 'low' cgroup..."
|
||||
mkdir -p /sys/fs/cgroup/cpuset/low
|
||||
echo 0 > /sys/fs/cgroup/cpuset/low/cpuset.cpus || echo "WARNING: Failed to write cpuset.cpus for low cgroup"
|
||||
echo 0 > /sys/fs/cgroup/cpuset/low/cpuset.mems || echo "WARNING: Failed to write cpuset.mems for low cgroup"
|
||||
echo 25 > /sys/fs/cgroup/cpuset/low/cpu.shares || echo "WARNING: Failed to write cpu.shares for low cgroup"
|
||||
|
||||
# Create "high" cgroup with 75% share
|
||||
echo "Configuring 'high' cgroup..."
|
||||
mkdir -p /sys/fs/cgroup/cpuset/high
|
||||
echo 0 > /sys/fs/cgroup/cpuset/high/cpuset.cpus || echo "WARNING: Failed to write cpuset.cpus for high cgroup"
|
||||
echo 0 > /sys/fs/cgroup/cpuset/high/cpuset.mems || echo "WARNING: Failed to write cpuset.mems for high cgroup"
|
||||
echo 75 > /sys/fs/cgroup/cpuset/high/cpu.shares || echo "WARNING: Failed to write cpu.shares for high cgroup"
|
||||
|
||||
echo "Cgroup initialization complete."
|
||||
echo " - low group: 25 shares (25% CPU on CPU 0)"
|
||||
echo " - high group: 75 shares (75% CPU on CPU 0)"
|
||||
;;
|
||||
|
||||
high)
|
||||
echo "Running ./max-cpu --single in 'high' cgroup (75% CPU share)..."
|
||||
if [ ! -f /sys/fs/cgroup/cpuset/high/tasks ]; then
|
||||
echo "ERROR: Cgroup is not initialized or mounted! Run './shared-cpu.sh init' first."
|
||||
exit 1
|
||||
fi
|
||||
echo $$ > /sys/fs/cgroup/cpuset/high/tasks || {
|
||||
echo "ERROR: Failed to add shell process ($$) to high cgroup!"
|
||||
exit 1
|
||||
}
|
||||
./max-cpu --single
|
||||
;;
|
||||
|
||||
low)
|
||||
echo "Running ./max-cpu --single in 'low' cgroup (25% CPU share)..."
|
||||
if [ ! -f /sys/fs/cgroup/cpuset/low/tasks ]; then
|
||||
echo "ERROR: Cgroup is not initialized or mounted! Run './shared-cpu.sh init' first."
|
||||
exit 1
|
||||
fi
|
||||
echo $$ > /sys/fs/cgroup/cpuset/low/tasks || {
|
||||
echo "ERROR: Failed to add shell process ($$) to low cgroup!"
|
||||
exit 1
|
||||
}
|
||||
./max-cpu --single
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown command: $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user