1. 背景

文章中说明了如何设置进程的优先级。这篇文章说一下如何设置进程的亲和性

亲和性是指进程和 CPU 核的亲和性。设置进程的亲和性后,程序只运行在设置的那几个核中,不会被调度到以外的核。

2. SetProcessAffinityMask() 说明

Win10 环境下,设置进程亲和性的接口为SetProcessAffinityMask(),函数原型如下:

BOOL SetProcessAffinityMask(
  [in] HANDLE    hProcess,
  [in] DWORD_PTR dwProcessAffinityMask
);

但是,这个函数在 go 中没有被实现,需要自己封装

3. 接口封装及测试

比如说,想让进程可以运行在 0、3、6 核上。

package main

import "time"
import "fmt"
import "syscall"
import "golang.org/x/sys/windows"

//封装接口
var modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
var procSetProcessAffinityMask = modkernel32.NewProc("SetProcessAffinityMask")

func SetProcessAffinityMask(s windows.Handle, processAffinityMask uint64) (err error) {
    r1, _, e1 := syscall.Syscall(procSetProcessAffinityMask.Addr(), 2, uintptr(s), uintptr(processAffinityMask), 0)
    if r1 == 0 {
        if e1 != 0 {
            err = e1
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

func main() {
    cur_task_handle := windows.CurrentProcess()
    err := windows.SetPriorityClass(cur_task_handle, windows.HIGH_PRIORITY_CLASS)
    if err != nil {
        fmt.Println(err)
        return
    }

    err = SetProcessAffinityMask(cur_task_handle, ((1 << 0) | (1 << 3) | (1 << 6)))
    if err != nil {
        fmt.Println(err)
    }

    for  {
        time.Sleep(10 * time.Second)
    }
}
处理器相关性

重点说明一下参数 processAffinityMask,这个参数是位有效,某一位置 1 表示进程可以运行在该 CPU 核,为 0 表示进程不能运行在该核。就像上述代码中,该参数的值为 ((1 << 0) | (1 << 3) | (1 << 6)),就表示程序可以运行在 0、3、6 核。

4. 总结

如果想要设置线程的亲和性,也可以按照上述方式进行封装。

import "syscall"
import "golang.org/x/sys/windows"

var modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
var procSetThreadAffinityMask = modkernel32.NewProc("SetThreadAffinityMask")
var procSetProcessAffinityMask = modkernel32.NewProc("SetProcessAffinityMask")

func SetThreadAffinityMask(s windows.Handle, threadAffinityMask uint64) (err error) {
    r1, _, e1 := syscall.Syscall(procSetThreadAffinityMask.Addr(), 2, uintptr(s), uintptr(threadAffinityMask), 0)
    if r1 == 0 {
        if e1 != 0 {
            err = e1
        } else {
            err = syscall.EINVAL
        }
    }
    return
}