Utilizzare la funzione sched_setaffinity in C
-
Usa la funzione
sched_setaffinity
per limitare l’esecuzione del processo a determinate CPU -
Usa la macro
CPU_SET
per indicare i core della CPU a cui associare il processo
Questo articolo spiegherà diversi metodi su come utilizzare la funzione sched_setaffinity
in C.
Usa la funzione sched_setaffinity
per limitare l’esecuzione del processo a determinate CPU
Al giorno d’oggi, l’hardware multi-core è onnipresente e i sistemi operativi devono gestire più processi in esecuzione simultaneamente su questi core. La parte del sistema operativo che si occupa della gestione dell’esecuzione di processi / thread è chiamata scheduler. Uno scheduler cerca di distribuire in modo efficiente tutti i processi / thread esistenti tra i core disponibili e allocare le fasce di tempo di conseguenza. La pianificazione è uno dei problemi di progettazione più difficili nei sistemi operativi, poiché è la principale garanzia di prestazioni per un determinato sistema. Non esiste un’interfaccia C standard per interagire con lo scheduler, ma alcuni sistemi operativi forniscono chiamate di sistema per modificare diversi parametri di pianificazione del processo.
sched_setaffinity
fa parte della libreria GNU C, ed è principalmente basato su funzionalità specifiche di Linux. La funzione imposta la cosiddetta CPU affinity mask, che indica l’insieme di core CPU su cui il processo può essere eseguito. sched_setaffinity
prende il valore PID come primo argomento e sizeof(cpu_set_t)
come secondo. Il terzo argomento è di tipo cpu_set_t
ed è una struttura opaca che deve essere manipolata usando le macro predefinite dall’intestazione <sched.h>
. Nota però, la macro _GNU_SOURCE
dovrebbe essere definita per rendere disponibili queste funzioni e macro. Nell’esempio seguente, implementiamo un programma che prende tre numeri interi dall’utente come argomenti della riga di comando e li memorizza per rappresentare rispettivamente i numeri di CPU del processo genitore / figlio e diverse iterazioni del bucle. Quindi, la macro CPU_ZERO
viene usata per cancellare la variabile cpu_set_t
, e fork
viene chiamato per generare un processo figlio.
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define errExit(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
int main(int argc, char *argv[]) {
cpu_set_t set;
int parentCPU, childCPU, wstatus;
long nloops;
if (argc != 4) {
fprintf(stderr, "Usage: %s parent-cpu child-cpu num-loops\n", argv[0]);
exit(EXIT_FAILURE);
}
parentCPU = strtol(argv[1], NULL, 0);
childCPU = strtol(argv[2], NULL, 0);
nloops = strtol(argv[3], NULL, 0);
CPU_ZERO(&set);
switch (fork()) {
case -1:
errExit("fork");
case 0:
CPU_SET(childCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");
for (int j = 0; j < nloops; j++) getpid();
exit(EXIT_SUCCESS);
default:
CPU_SET(parentCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");
for (int j = 0; j < nloops; j++) getpid();
wait(NULL);
exit(EXIT_SUCCESS);
}
}
Usa la macro CPU_SET
per indicare i core della CPU a cui associare il processo
La funzione sched_setaffinity
viene chiamata per processo o thread; quindi, una volta che il fork
ritorna, possiamo specificare le diverse maschere della CPU per i processi genitore e figlio. La macro CPU_SET
è usata per modificare la struttura cpu_set_t
precedentemente azzerata e di conseguenza passarla alla chiamata sched_setaffinity
. Nota che ogni processo esegue un bucle in cui chiama getpid
per occupare le risorse della CPU e rendere più semplice la dimostrazione dell’esempio. Il processo genitore attende il figlio con la chiamata wait
nell’esempio precedente e usando waitpid
nel successivo. Se vuoi osservare il comportamento dimostrato, puoi guardare i processi di sistema usando l’utility della riga di comando htop
, ampiamente disponibile sui sistemi Linux.
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define errExit(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
int main(int argc, char *argv[]) {
cpu_set_t set;
int parentCPU, childCPU, wstatus;
long nloops;
if (argc != 4) {
fprintf(stderr, "Usage: %s parent-cpu child-cpu num-loops\n", argv[0]);
exit(EXIT_FAILURE);
}
parentCPU = strtol(argv[1], NULL, 0);
childCPU = strtol(argv[2], NULL, 0);
nloops = strtol(argv[3], NULL, 0);
CPU_ZERO(&set);
pid_t c_pid = fork();
if (c_pid == -1) errExit("fork");
switch (c_pid) {
case -1:
errExit("fork");
case 0:
CPU_SET(childCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");
for (int j = 0; j < nloops; j++) getpid();
exit(EXIT_SUCCESS);
default:
CPU_SET(parentCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");
for (int j = 0; j < nloops; j++) getpid();
if (waitpid(c_pid, &wstatus, WUNTRACED | WCONTINUED) == -1)
errExit("waitpid");
exit(EXIT_SUCCESS);
}
}
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn Facebook