Utilice la función sched_setaffinity en C

Jinku Hu 12 octubre 2023
  1. Utilice la función sched_setaffinity para limitar la ejecución del proceso a determinadas CPU (s)
  2. Utilice la macro CPU_SET para indicar los núcleos de la CPU a los que enlazar el proceso
Utilice la función sched_setaffinity en C

Este artículo explicará varios métodos de cómo utilizar la función sched_setaffinity en C.

Utilice la función sched_setaffinity para limitar la ejecución del proceso a determinadas CPU (s)

Hoy en día, el hardware de múltiples núcleos es omnipresente y los sistemas operativos necesitan administrar múltiples procesos que se ejecutan simultáneamente en estos núcleos. La parte del sistema operativo que se ocupa de administrar la ejecución de procesos / subprocesos se denomina programador. Un programador intenta distribuir de manera eficiente todos los procesos / subprocesos existentes en los núcleos disponibles y asignar los intervalos de tiempo en consecuencia. La programación es uno de los problemas de diseño más difíciles en los sistemas operativos, ya que es la principal garantía de rendimiento para el sistema dado. No hay una interfaz C estándar para interactuar con el programador, pero ciertos sistemas operativos proporcionan llamadas al sistema para modificar varios parámetros de programación de procesos.

sched_setaffinity es parte de la biblioteca GNU C, y se basa principalmente en funciones específicas de Linux. La función establece la denominada máscara de afinidad de CPU, que indica el conjunto de núcleos de CPU en los que el proceso puede ejecutarse. sched_setaffinity toma el valor PID como primer argumento y sizeof(cpu_set_t) como el segundo. El tercer argumento es de tipo cpu_set_t y es una estructura opaca que necesita ser manipulada usando las macros predefinidas del encabezado <sched.h>. Sin embargo, tenga en cuenta que la macro _GNU_SOURCE debe definirse para que estas funciones y macros estén disponibles. En el siguiente ejemplo, implementamos un programa que toma tres números enteros del usuario como argumentos de la línea de comandos y los almacena para representar los números de CPU del proceso padre / hijo y varias iteraciones de bucle, respectivamente. Luego, la macro CPU_ZERO se usa para borrar la variable cpu_set_t, y se llama a fork para generar un proceso hijo.

#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);
  }
}

Utilice la macro CPU_SET para indicar los núcleos de la CPU a los que enlazar el proceso

La función sched_setaffinity se llama por proceso o hilo; por lo tanto, una vez que regresa el fork, podemos especificar las diferentes máscaras de CPU para los procesos padre e hijo. La macro CPU_SET se utiliza para modificar la estructura cpu_set_t previamente puesta a cero y, en consecuencia, pasarla a la llamada sched_setaffinity. Tenga en cuenta que cada proceso ejecuta un bucle en el que llaman a getpid para ocupar los recursos de la CPU y facilitar la demostración del ejemplo. El proceso padre espera al hijo con la llamada wait en el ejemplo anterior y usando waitpid en el siguiente. Si desea observar el comportamiento demostrado, puede observar los procesos del sistema utilizando la utilidad de línea de comandos htop, ampliamente disponible en sistemas 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);
  }
}
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

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

Artículo relacionado - C Process