CPU Affinity

1 篇文章 0 订阅
1 篇文章 0 订阅

为了测试笔记本在CPU高负荷运转的时候,风扇是否正常工作,需要模拟CPU高负荷的情况.由于笔记本是四核的,因此需要使4个CPU都忙碌起来.因为之前阅读过Nginx的源码,其中就有一个参数是用于配置CPU亲和性,保证启动的进程运行在不同的CPU上,所以考虑使用CPU亲和性保证所有的CPU都处于忙碌状态.

什么是CPU的亲和性?

CPU亲合力就是指在Linux系统中能够将一个或多个进程绑定到一个或多个处理器上运行.
一个进程的CPU亲合力掩码决定了该进程将在哪个或哪几个CPU上运行.在一个多处理器系统中,设置CPU亲合力的掩码可能会获得更好的性能.


简单的说就是人工的进行负载均衡,而不是将这个任务(cpu分配)交给CPU调度器.

下面定义了设置CPU亲和性所需要的函数和宏,在我的笔记本上(Ubuntu 12.04 LTS, Kernel Linux 3.8.0-35-generic)这些内容在文件/usr/include/sched.h中.

/* Definitions for POSIX 1003.1b-1993 (aka POSIX.4) scheduling interface.
   Copyright (C) 1996,1997,1999,2001-2004,2007,2010
   Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#ifndef	_SCHED_H
#define	_SCHED_H	1

#include <features.h>

/* Get type definitions.  */
#include <bits/types.h>

#define __need_size_t
#include <stddef.h>

#define __need_time_t
#define __need_timespec
#include <time.h>

#ifndef __pid_t_defined
typedef __pid_t pid_t;
# define __pid_t_defined
#endif


/* Get system specific constant and data structure definitions.  */
#include <bits/sched.h>
/* Define the real names for the elements of `struct sched_param'.  */
#define sched_priority	__sched_priority


__BEGIN_DECLS

/* Set scheduling parameters for a process.  */
extern int sched_setparam (__pid_t __pid, __const struct sched_param *__param)
     __THROW;

/* Retrieve scheduling parameters for a particular process.  */
extern int sched_getparam (__pid_t __pid, struct sched_param *__param) __THROW;

/* Set scheduling algorithm and/or parameters for a process.  */
extern int sched_setscheduler (__pid_t __pid, int __policy,
			       __const struct sched_param *__param) __THROW;

/* Retrieve scheduling algorithm for a particular purpose.  */
extern int sched_getscheduler (__pid_t __pid) __THROW;

/* Yield the processor.  */
extern int sched_yield (void) __THROW;

/* Get maximum priority value for a scheduler.  */
extern int sched_get_priority_max (int __algorithm) __THROW;

/* Get minimum priority value for a scheduler.  */
extern int sched_get_priority_min (int __algorithm) __THROW;

/* Get the SCHED_RR interval for the named process.  */
extern int sched_rr_get_interval (__pid_t __pid, struct timespec *__t) __THROW;


#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 根据掩码,设置CPU亲和性*/
extern int sched_setaffinity (__pid_t __pid, size_t __cpusetsize,
			      __const cpu_set_t *__cpuset) __THROW;

/* Get the CPU affinity for a task 获取CPU亲和性放置到掩码中*/
extern int sched_getaffinity (__pid_t __pid, size_t __cpusetsize,
			      cpu_set_t *__cpuset) __THROW;
#endif

__END_DECLS

#endif /* sched.h */

其中CPU_ZERO用于清空掩码对应的CPU集合.

CPU_SET用于将某个CPU号添加到掩码中.

CPU_CLR用于将某个CPU号从掩码中清除.

CPU_ISSET用于判断某个CPU号是否在掩码中.


而其中使用到的关键的数据结构cpu_set_t,定义在/usr/include/x86_64-linux-gnu/bits/sched.h中,

/* Data structure to describe CPU mask.  */
typedef struct
{
  __cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
} cpu_set_t;

下面定义几个基本函数和常量,

/* Size definition for CPU sets.  */
# define __CPU_SETSIZE  1024
# define __NCPUBITS     (8 * sizeof (__cpu_mask))

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

/* Basic access functions.  */
# define __CPUELT(cpu)  ((cpu) / __NCPUBITS)
# define __CPUMASK(cpu) ((__cpu_mask) 1 << ((cpu) % __NCPUBITS))


可以看出其实cpu_set_t就是(在我的笔记本上)

unsigned long int __bits [1024 / (8 * 8)]

也就是说cpu_set_t其实就是大小为16的long数组.数组中的每一个long,都可以表示64个cpu(每一个bit表示一个cpu),也就是说总共可以表示16 * 64 = 1024个cpu.

# define CPU_SET(cpu, cpusetp) __CPU_SET_S (cpu, sizeof (cpu_set_t), cpusetp)

# define CPU_SET_S(cpu, setsize, cpusetp)   __CPU_SET_S (cpu, setsize, cpusetp)

# define __CPU_SET_S(cpu, setsize, cpusetp) \
  (__extension__                                                              \
   ({ size_t __cpu = (cpu);                                                   \
      __cpu < 8 * (setsize)                                                   \
      ? (((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)]               \
         |= __CPUMASK (__cpu))                                                \
      : 0; }))

__CPUELT(cpu)用于定位该cpu在数组中的位置, 比如CPU0位于第一个long,CPU64位于第二个long,CPU128位于第三个long,依次类推.

__CPUMASK(cpu)表示位于该long中的第几个bit.

比如CPU1,__CPUMASK(1) = 0x10 = 2,表示第二个绑定到第二个CPU.如果原来的__bits[0] = 1(绑定到CPU0), __CPUMASK(1) = 2, 那么__bits[0] | __CPUMASK[1] = 

0x11 = 3, 表示绑定到CPU0和CPU1上.

上面的解释可能抽象了一些, 下面用一个例子形象的表达是如何表示的?

On most systems, Linux included, the interface for setting CPU affinity uses a bitmask. A bitmask is a series of nbits, where each bit individually corresponds to the status of some other object. For example, CPU affinity (on 32-bit machines) is represented by a 32-bit bitmask. Each bit represents whether the given task is bound to the corresponding processor. Count the bits from right to left, bit 0 to bit 31 and, thus, processor zero to processor 31. For example:

<span style="background-color: rgb(204, 204, 204);"><tt>11111111111111111111111111111111 = 4,294,967,295
</tt>
</span>

is the default CPU affinity mask for all processes. Because all bits are set, the process can run on any processor. Conversely:

<span style="background-color: rgb(204, 204, 204);"><tt>00000000000000000000000000000001 = 1
</tt>
</span>
is much more restrictive. Only bit 0 is set, so the process may run only on processor zero. That is, this affinity mask binds a process to processor zero. 

Get it? What do the next two masks equal in decimal? What is the result of using them as the affinity mask of a process?

<span style="background-color: rgb(204, 204, 204);"><tt>10000000000000000000000000000000
00000000000000000000000000000011
</tt>
</span>

The first is equal to 2,147,483,648 and, because bit 31 is set, binds the process to processor number 31. The second is equal to 3, and it binds the process in question to processor zero and processor one.

The Linux CPU affinity interface uses a bitmask like that shown above. Unfortunately, C does not support binary constants, so you always have to use the decimal or hexadecimal equivalent. You may get a compiler warning for very large decimal constants that set bit 31, but they will work. 

我们看看使用CPU亲和性是否可以达到我们的要求.

设置所有CPU忙,

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/sysinfo.h>

#define __USE_GNU
#include <sched.h>
#include <ctype.h>

int main(){
        int i_cpu_num;
        int i;
        // 获取cpu数目
        i_cpu_num = sysconf(_SC_NPROCESSORS_CONF);
        printf("_SC_NPROCESSORS_CONF=%d\n", i_cpu_num);
        for(i = 0; i < i_cpu_num - 1; ++i){
                // fork出新的进程
                int i_pid = fork();
                // 进程为子进程
                if (i_pid == 0){
                        break;
                }
        }
        printf("new thread %d start", i);
        // 设置cpu亲和性 
        cpu_set_t mask;
        CPU_ZERO(&mask);
        CPU_SET(i, &mask);
        if (sched_setaffinity(0, sizeof(mask), &mask) == -1){
                printf("set affinity failed");
        }
        // 空转消耗cpu
        while(1){

        }
        return 0;
}
         

运行代码后,


可以看到4个CPU都基本上处于100%的状态.

也许有人会考虑到如果不设置亲和性,会出现什么结果呢?能否时所有CPU都处于忙碌呢?

似乎也可以达到我们的目的.

那么CPU亲和性有什么用处呢?我们可以使用它完成更精确的控制,看一下下面的例子.

如果只想使的CPU利用率达到50%呢?也就是只有两个CPU得到满负荷使用.

将CPU_SET(i, &mask)修改为CPU_SET(i % 2, &mask),也就是偶数的进程运行在CPU0上,奇数的进程运行在CPU1上.

那么运行结果是否如我们预期呢?


基本上只有CPU0和CPU1达到了100%.

而且PID4892-4893的CPU占用率均为50%,也就是说两个进场占用了CPU0,两个进程占用了CPU1.而之前的程序四个进程的CPU占用率都是100%.

更多内容可以看一下IBM上的文章

http://www.ibm.com/developerworks/cn/linux/l-affinity.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值