程序绑核和CPU亲和性

195次阅读  |  发布于2月以前

我们常常使用 taskset 来将应用程序绑定到一个核或者多个核上,这种机制叫做 CPU 亲和性 (CPU affinity)。

taskset 是一个 Linux 命令行工具,用于设置或检索一个进程的 CPU 亲和性(CPU affinity)。CPU 亲和性决定了一个进程可以运行在哪个 CPU 或 CPU 核心上。 合理设置 CPU 亲和性可以带来以下好处:

taskset 使用方法

taskset 命令的主要用法如下。

  1. 获取进程的 CPU 亲和性
taskset -p <PID>

该命令会显示进程 ID 为 <PID> 的进程当前的 CPU 亲和性掩码 (affinity mask)。

  1. 设置进程的 CPU 亲和性
taskset -p <MASK> <PID>

该命令将进程 ID 为 <PID> 的进程的 CPU 亲和性设置为 <MASK> 指定的 CPU 掩码。

CPU 掩码是一个十六进制数, 用于表示要被绑定的 CPU 核心。例如, 在一个 4 核心的系统中, 0x1 表示只绑定到第一个 CPU 核心, 0x5 表示绑定到第 1 个和第 3 个 CPU 核心。哪一个位上为 1,就会绑定到哪个核上。

  1. 启动新进程并设置其 CPU 亲和性
taskset <MASK> <COMMAND> [ARGUMENTS...]

该命令会使用 <MASK> 指定的 CPU 亲和性启动一个新进程, 运行命令 <COMMAND> 及其参数 [ARGUMENTS...]

  1. 其他选项

taskset 还提供了其他一些有用的选项:

例子:

默认行为是运行一条新命令:
    taskset 03 sshd -b 1024
您可以获取现有任务的掩码:
    taskset -p 700
或设置掩码:
    taskset -p 03 700
使用逗号分隔的列表格式而不是掩码:
    taskset -pc 0,3,7-11 700
列表格式中的范围可以带一个跨度参数:
    例如 0-31:2 与掩码 0x55555555 等效

子进程的 CPU 亲和性

Linux 默认子进程会继承父进程的 CPU 亲和性,以保持亲和性的连续性。

但如果有需要, 用户依然可以手动设置属于它的子进程或线程的 CPU 亲和性,以优化 CPU 资源利用和程序性能。

如果想修改其他用户的进程的 CPU 亲和性,需要 root 权限或者拥有 CAP_SYS_NICE 权限。

任何用户都可以获取任意进程的 CPU 亲和性掩码。

运行时设置 CPU 亲和性

当然,你的程序在运行时,也可以调用 taskset 命令设置 CPU 亲和性,比如下面的 Go 代码, 会将程序本身绑定到前两个 CPU 核上:

package main

import (
 "fmt"
 "os"
 "os/exec"
)

func main() {
 pid := os.Getpid()

 // 获取当前进程的 CPU 亲和性
 cmd := exec.Command("taskset", "-p", fmt.Sprintf("%d", pid))
 out, err := cmd.Output()
 if err != nil {
  fmt.Println(err)
 }

 // 设置当前进程的 CPU 亲和性为 CPU 0 和 CPU 1
 cmd = exec.Command("taskset", "-p", "0,1", fmt.Sprintf("%d", pid))
 out, err = cmd.Output()
 if err != nil {
  fmt.Println(err)
 }
 fmt.Printf("%s", out)
}

实际上,你也可以调用系统调用sched_setaffinity来绑核,使用sched_getaffinity来查询 CPU 的亲和性:

package main

import (
 "fmt"
 "syscall"
 "time"
 "unsafe"
)

func main() {
 var mask uintptr

 // 获取当前进程的 CPU 亲和性
 if _, _, err := syscall.RawSyscall(syscall.SYS_SCHED_GETAFFINITY, 0, uintptr(unsafe.Sizeof(mask)), uintptr(unsafe.Pointer(&mask))); err != 0 {
  fmt.Println("failed to get CPU affinity:", err)
  return
 }
 fmt.Println("current CPU affinity:", mask)

 // 设置当前进程的 CPU 亲和性为 CPU 0 和 CPU 1
 mask = 3
 if _, _, err := syscall.RawSyscall(syscall.SYS_SCHED_SETAFFINITY, 0, uintptr(unsafe.Sizeof(mask)), uintptr(unsafe.Pointer(&mask))); err != 0 {
  fmt.Println("failed to set CPU affinity:", err)
  return
 }
 fmt.Println("new CPU affinity:", mask)

 for {
  println("Hello, World!")
  time.Sleep(1 * time.Second)
 }
}

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8