Use a função sched_setaffinity em C

Jinku Hu 12 outubro 2023
  1. Use a função sched_setaffinity para limitar a execução do processo a certas CPU (s)
  2. Use a macro CPU_SET para indicar os núcleos da CPU para vincular o processo a
Use a função sched_setaffinity em C

Este artigo irá explicar vários métodos de como usar a função sched_setaffinity em C.

Use a função sched_setaffinity para limitar a execução do processo a certas CPU (s)

Hoje em dia, o hardware multinúcleo é onipresente e os sistemas operacionais precisam gerenciar vários processos executados simultaneamente nesses núcleos. A parte do sistema operacional que lida com o gerenciamento da execução do processo / threads é chamada de planejador. Um planejador tenta distribuir com eficiência todos os processos / threads existentes entre os núcleos disponíveis e alocar as fatias de tempo de acordo. O escalonamento é um dos problemas de design mais difíceis em sistemas operacionais, pois é a principal garantia de desempenho de um determinado sistema. Não há interface C padrão para interagir com o agendador, mas certos sistemas operacionais fornecem chamadas de sistema para modificar vários parâmetros de agendamento de processo.

sched_setaffinity é parte da biblioteca GNU C e é principalmente baseado em funcionalidades específicas do Linux. A função define a chamada máscara de afinidade da CPU, que indica o conjunto de núcleos da CPU nos quais o processo pode ser executado. sched_setaffinity leva o valor PID como o primeiro argumento e sizeof(cpu_set_t)como o segundo. O terceiro argumento é do tipo cpu_set_t e é uma estrutura opaca que precisa ser manipulada usando as macros predefinidas do cabeçalho <sched.h>. Observe, porém, que a macro _GNU_SOURCE deve ser definida para tornar essas funções e macros disponíveis. No exemplo a seguir, implementamos um programa que pega três inteiros do usuário como argumentos de linha de comando e os armazena para representar os números da CPU do processo pai / filho e várias iterações de loop, respectivamente. Então, a macro CPU_ZERO é usada para limpar a variável cpu_set_t, e fork é chamado para gerar um processo filho.

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

Use a macro CPU_SET para indicar os núcleos da CPU para vincular o processo a

a função sched_setaffinity é chamada por processo ou thread; portanto, quando o fork retorna, podemos especificar as diferentes máscaras de CPU para os processos pai e filho. A macro CPU_SET é usada para modificar a estrutura cpu_set_t previamente zerada e, conseqüentemente, passá-la para a chamada sched_setaffinity. Observe que cada processo executa um loop no qual chama getpid para ocupar recursos da CPU e tornar mais fácil demonstrar o exemplo. O processo pai espera pelo filho com a chamada wait no exemplo anterior e usando waitpid no próximo. Se você quiser observar o comportamento demonstrado, pode observar os processos do sistema usando o utilitário de linha de comando htop, amplamente disponível em 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

Artigo relacionado - C Process