Utilizzare la funzione sched_setaffinity in C
-
Usa la funzione
sched_setaffinityper limitare l’esecuzione del processo a determinate CPU -
Usa la macro
CPU_SETper 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