在 C 語言中使用 sched_setaffinity 函式

Jinku Hu 2023年10月12日
  1. 使用 sched_setaffinity 函式將程序執行限制在特定的 CPU 上
  2. 使用 CPU_SET 巨集指示 CPU 核心將程序繫結到
在 C 語言中使用 sched_setaffinity 函式

本文將介紹幾種如何在 C 語言中使用 sched_setaffinity 函式的方法。

使用 sched_setaffinity 函式將程序執行限制在特定的 CPU 上

如今,多核硬體無處不在,作業系統需要管理在這些核上同時執行的多個程序。作業系統中負責管理程序/執行緒執行的部分稱為排程程式。排程程式嘗試在可用核心之間有效地分配所有現有程序/執行緒,並相應地分配時間片。排程是作業系統中最困難的設計問題之一,因為它是給定系統的主要效能保證。沒有與排程程式互動的標準 C 介面,但是某些 OS 提供了系統呼叫來修改多個流程排程引數。

sched_setaffinity 是 GNU C 庫的一部分,它主要基於特定於 Linux 的功能。該函式設定所謂的 CPU 親和力掩碼,它表示可以在其上執行程序的 CPU 核心集。sched_setaffinity 將 PID 值作為第一個引數,並將 sizeof(cpu_set_t) 作為第二個引數。第三個引數是 cpu_set_t 型別,它是一個不透明的結構,需要使用 <sched.h> 標頭檔案中的預定義巨集進行操作。但是請注意,應定義 _GNU_SOURCE 巨集以使這些功能和巨集可用。在下面的示例中,我們實現了一個程式,該程式將來自使用者的三個整數用作命令列引數,並將其儲存以分別表示父/子程序 CPU 編號和幾次迴圈迭代。然後,使用 CPU_ZERO 巨集清除 cpu_set_t 變數,並呼叫 fork 產生一個子程序。

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

使用 CPU_SET 巨集指示 CPU 核心將程序繫結到

sched_setaffinity 函式是每個程序或執行緒呼叫的;因此,一旦 fork 返回,我們就可以為父程序和子程序指定不同的 CPU 掩碼。CPU_SET 巨集用於修改先前歸零的 cpu_set_t 結構體,然後將其傳遞給 sched_setaffinity 呼叫。請注意,每個程序執行一個迴圈,在其中它們呼叫 getpid 來佔用 CPU 資源並簡化示例的演示。父程序在上一個示例中使用 wait 呼叫來等待子級,在下一個示例中使用 waitpid 來等待子級。如果你想觀察所演示的行為,則可以使用 htop 命令列實用程式來觀察系統程序,該實用程式在 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);
  }
}
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 創辦人。Jinku 在機器人和汽車行業工作了8多年。他在自動測試、遠端測試及從耐久性測試中創建報告時磨練了自己的程式設計技能。他擁有電氣/ 電子工程背景,但他也擴展了自己的興趣到嵌入式電子、嵌入式程式設計以及前端和後端程式設計。

LinkedIn Facebook

相關文章 - C Process