首页 > 代码库 > c语言设置cpu affinity (设置程序需要使用的cpu内核) cpu mask

c语言设置cpu affinity (设置程序需要使用的cpu内核) cpu mask

最近打算写个小程序, 需要控制使用的是哪个 cpu 内核,所以做了一些调查, 整理一下分享给大家。 

ps: 因为毕业季很久没有写博客了, 以后继续。 顺便鄙视一下那些转载不声明出处的, by watkins.song

pps: 最近有了个新的id, 因为在Oracle, wei.x.song, 不知到以后用哪个id比较酷一点


主要参考: 

http://www.gnu.org/software/libc/manual/html_node/CPU-Affinity.html/

http://stackoverflow.com/questions/7296963/gnu-source-and-use-gnu

https://gcc.gnu.org/ml/fortran/2005-10/msg00365.html


设置想要使用的cpu内核, 因为现在绝大部分都是多内核的cpu, 其实就是设置cpu的affinity,

网上找的例子这样写:

#include <stdio.h>
#include <math.h>

#include <unistd.h>
#include <sys/times.h>
#define __USE_GNU
#include <sched.h>

但是, 可以很确定的说,这种写法是不对的, 因为在https://gcc.gnu.org/ml/fortran/2005-10/msg00365.html 这里,很多年以前就说了不能由用户直接定义 __USE_GNU的宏定义, 虽然这样做可以正常使用, 能够编译通过, 但是违反了一些原则,在工程里面就不能这么使用了。


先看一下我总结的代码:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <unistd.h>
#include <sys/times.h>

void set_cpu(int id);

int main(void)
{
	puts("cpu affinity set");
	set_cpu(0x0003);

	while(1)
	{
		//sleep(10);
		//printf("in the while\n");
	}

	return 1;
}

// setup the cpu set of this program to run on
void set_cpu(int id)
{
    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(id, &mask);
    if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
    {
        fprintf(stderr, "warning: could not set CPU affinity/n");
    }
}


#define _GNU_SOURCE

这个宏定义一定要放在代码文件的最开始位置, 即使你把它放在了<sched.h>之前,但是如果不是文件最开始部分,那么也编译不了。


看一下系统的运行:



可以看到在指定的cpu上运行的程序。


然后, 看一下具体的一些细节。

cpu_set_t 是一个cpu集合, 用于表明使用哪些内核, 所以也可以称为是mask, cpu_set_t的实现如下:

typedef struct
{
  __cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
} cpu_set_t;

是一个结构体,  __cpu_mask的定义为:

/* Type for array elements in 'cpu_set_t'.  */
typedef unsigned long int __cpu_mask;

__cpu_mask是一个unsigned long int类型。


运行的时候, 没有运行CPU_ZERO() 之前 __bits = {0, 140737351860832, 10, 140737354104832, 17, 140737348450122, 16, 17, 10, 4196320, 140737488347648, 140737348456117, 140737351860832, 140737348456959, 140737351860832, 16}


然后运行CPU_ZERO() 将cpu_set都重置为0, 这个时候:

__bits = {0 <repeats 16 times>}


sched_setaffinity(0, sizeof(mask), &mask) == -1 这句代码则根据已经设置的cpu mask设置当前进程采用哪个cpu内核运行。

sched_setaffinity()的原型函数为:

int sched_setaffinity (pid_t pid, size_t cpusetsize, const cpu_set_t *cpuset)


This function installs the cpusetsize bytes long affinity mask pointed to by cpuset for the process or thread with the ID pid. If successful the function returns zero and the scheduler will in future take the affinity information into account.

If the function fails it will return -1 and errno is set to the error code:

ESRCH
No process or thread with the given ID found. 
EFAULT
The pointer cpuset is does not point to a valid object. 
EINVAL
The bitset is not valid. This might mean that the affinity set might not leave a processor for the process or thread to run on.

This function is a GNU extension and is declared in sched.h.


来看一下  sched.h 头文件, 头文件中有这样一段:

#ifdef __USE_GNU
/* Access macros for `cpu_set'.  */
# define CPU_SETSIZE __CPU_SETSIZE
# define CPU_SET(cpu, cpusetp)	 __CPU_SET_S (cpu, sizeof (cpu_set_t), cpusetp)
# define CPU_CLR(cpu, cpusetp)	 __CPU_CLR_S (cpu, sizeof (cpu_set_t), cpusetp)
# define CPU_ISSET(cpu, cpusetp) __CPU_ISSET_S (cpu, sizeof (cpu_set_t), 						cpusetp)
# define CPU_ZERO(cpusetp)	 __CPU_ZERO_S (sizeof (cpu_set_t), cpusetp)
# define CPU_COUNT(cpusetp)	 __CPU_COUNT_S (sizeof (cpu_set_t), cpusetp)

# define CPU_SET_S(cpu, setsize, cpusetp)   __CPU_SET_S (cpu, setsize, cpusetp)
# define CPU_CLR_S(cpu, setsize, cpusetp)   __CPU_CLR_S (cpu, setsize, cpusetp)
# define CPU_ISSET_S(cpu, setsize, cpusetp) __CPU_ISSET_S (cpu, setsize, 							   cpusetp)
# define CPU_ZERO_S(setsize, cpusetp)	    __CPU_ZERO_S (setsize, cpusetp)
# define CPU_COUNT_S(setsize, cpusetp)	    __CPU_COUNT_S (setsize, cpusetp)

# define CPU_EQUAL(cpusetp1, cpusetp2)   __CPU_EQUAL_S (sizeof (cpu_set_t), cpusetp1, cpusetp2)
# define CPU_EQUAL_S(setsize, cpusetp1, cpusetp2)   __CPU_EQUAL_S (setsize, cpusetp1, cpusetp2)

# define CPU_AND(destset, srcset1, srcset2)   __CPU_OP_S (sizeof (cpu_set_t), destset, srcset1, srcset2, &)
# define CPU_OR(destset, srcset1, srcset2)   __CPU_OP_S (sizeof (cpu_set_t), destset, srcset1, srcset2, |)
# define CPU_XOR(destset, srcset1, srcset2)   __CPU_OP_S (sizeof (cpu_set_t), destset, srcset1, srcset2, ^)
# define CPU_AND_S(setsize, destset, srcset1, srcset2)   __CPU_OP_S (setsize, destset, srcset1, srcset2, &)
# define CPU_OR_S(setsize, destset, srcset1, srcset2)   __CPU_OP_S (setsize, destset, srcset1, srcset2, |)
# define CPU_XOR_S(setsize, destset, srcset1, srcset2)   __CPU_OP_S (setsize, destset, srcset1, srcset2, ^)

# define CPU_ALLOC_SIZE(count) __CPU_ALLOC_SIZE (count)
# define CPU_ALLOC(count) __CPU_ALLOC (count)
# define CPU_FREE(cpuset) __CPU_FREE (cpuset)


/* Set the CPU affinity for a task */
extern int sched_setaffinity (__pid_t __pid, size_t __cpusetsize,
			      __const cpu_set_t *__cpuset) __THROW;

/* Get the CPU affinity for a task */
extern int sched_getaffinity (__pid_t __pid, size_t __cpusetsize,
			      cpu_set_t *__cpuset) __THROW;
#endif

如果定义了 __USE_GNU 的话, 则定义以下若干宏定义,  但是我们说了, 用户不能定义 __USE_GNU这个宏, 那么这是在哪里的呢?  sched.h最开始包含了 features.h 这个头文件, 在这个头文件中有如下定义:

#ifdef	_GNU_SOURCE
# define __USE_GNU	1
#endif

所以在我们程序的最开始位置, 定义了 _GNU_SOURCE 这个宏定义