现在的位置: 首页 > 综合 > 正文

设置linux进程优先级和CPU亲和性(转载)

2012年09月16日 ⁄ 综合 ⁄ 共 12500字 ⁄ 字号 评论关闭

进程cpu资源分配就是指进程的优先权(priority)。优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

一、先看系统进程:

PR 就是 Priority 的简写,而 NI 是 nice 的简写。这两个值决定了PR的值,PR越小,进程优先权就越高,就越“优先执行”。换算公式为:PR(new) = PR(old) + NI

---------------------------------------------------------------------------
二、修改进程优先级的命令主要有两个:nice,renice

1、一开始执行程序就指定nice值:nice

Java代码

  1. nice -n -5 /usr/local/mysql/bin/mysqld_safe &

linux nice 命令详解

  功能说明:设置优先权。

  语  法:nice [-n <优先等级>][--help][--version][执行指令]

  补充说明:nice指令可以改变程序执行的优先权等级。

  参  数:-n<优先等级>或-<优先等级>或--adjustment=<优先等级>  设置欲执行的指令的优先权等级。等级的范围从-20-19,其中-20最高,19最低,只有系统管理者可以设置负数的等级。

   --help  在线帮助。
   --version  显示版本信息。
---------------------------------------------------------------------------
2.1、调整已存在进程的nice:renice

Java代码

  1. renice -5 -p 5200
  2. #PID为5200的进程nice设为-5

linux renice 命令详解

  功能说明:调整优先权。

  语  法:renice [优先等级][-g <程序群组名称>...][-p <程序识别码>...][-u <用户名称>...]

  补充说明:renice指令可重新调整程序执行的优先权等级。预设是以程序识别码指定程序调整其优先权,您亦可以指定程序群组或用户名称调整优先权等级,并修改所有隶属于该程序群组或用户的程序的优先权。等级范围从-20--19,只有系统管理者可以改变其他用户程序的优先权,也仅有系统管理者可以设置负数等级。

  参  数:
  -g <程序群组名称>  使用程序群组名称,修改所有隶属于该程序群组的程序的优先权。
  -p <程序识别码>  改变该程序的优先权等级,此参数为预设值。
  -u <用户名称>  指定用户名称,修改所有隶属于该用户的程序的优先权。

2.2、也可以用top命令更改已存在进程的nice:

Java代码

  1. top
  2. #进入top后按"r"-->输入进程PID-->输入nice值

--------------------------------------------------------------------------
三、把进程运行到指定CPU(即修改进程的"CPU亲和性"):taskset
两个名词
SMP (Symmetrical Multi-Processing):指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。 [更多...]

CPU affinity:中文唤作“CPU亲和性”,是指在CMP架构下,能够将一个或多个进程绑定到一个或多个处理器上运行。[更多...]

请先确定你的cpu核心及命名(例如四个核心:0,1,2,3):cat /proc/cpuinfo

Java代码

  1. taskset -cp 1 5200
  2. #把PID为5200的进程运行到CPU#1上
  3. #也可以在启动进程时指定:
  4. taskset -c 1 /etc/init.d/mysql start

linux taskset命令详解

SYNOPSIS
taskset [options] [mask | list ] [pid | command [arg]...]
OPTIONS
-p, --pid
operate on an existing PID and not launch a new task
-c, --cpu-list
specifiy a numerical list of processors instead of a bitmask.
The list may contain multiple items, separated by comma, and
ranges. For example, 0,5,7,9-11.
-h, --help
display usage information and exit
-V, --version
output version information and exit

 

 

 

 

=================================================================

手工修改linux进程的CPU调度

现在多核的CPU已经相当普遍了,那么这种多核的服务器如何让CPU得到充分利用,可以靠应用自己来定义,或者依赖操作系统来调度。根据红帽的说法RHEL5有一个很强壮的CPU调度机制,RHEL6就更强壮了,所以看起来跑在LINUX下面的应用应该都不用去管该用哪个CPU。

首先我们来看看CPU中断请求的统计:

CentOS release 5.2 (Final)

手工修改linux进程的CPU调度 - 德哥(DiGoal,Just Do It!) - Not Only DBA

从图上看,CPU的使用基本上还是均匀的。不过CPU0负载还是最大的。

所有在某种情况下可能会需要手工来设置进程使用CPU核的优先级。

下面是一个操作的例子:

postgres 6457 1 0 May05 ? 00:00:00 /app/pgsql/bin/postgres -D /database/pgdata -p 1921

[root@develop1 ~]# taskset -pc 6457

pid 6457’s current affinity list: 0-3

这个进程目前是默认与0-3 这4个核心亲和的。也就是说会在0-3这几个核心调度。

[root@develop1 ~]# taskset -pc 0-1 6457

pid 6457’s current affinity list: 0-3

pid 6457’s new affinity list: 0,1

修改之后我们看到,已经修改为0,1的范围了。

可以通过top -p 6457 [f -> j]

查看P列可以看到当前运行的核心号。

如果该成在单个CORE上跑的话,马上就能看到CORE的变化。

下面是taskset的MAN PAGE:

从描述上来看的话,只要taskset返回结果了,那LINUX肯定是确保得到了你想要的结果。

DESCRIPTION

taskset is used to set or retrieve the CPU affinity of a running process given its PID or to launch a new COM-

MAND with a given CPU affinity. CPU affinity is a scheduler property that “bonds” a process to a given set of

CPUs on the system. The Linux scheduler will honor the given CPU affinity and the process will not run on any

other CPUs. Note that the Linux scheduler also supports natural CPU affinity: the scheduler attempts to keep

processes on the same CPU as long as practical for performance reasons. Therefore, forcing a specific CPU

affinity is useful only in certain applications.

The CPU affinity is represented as a bitmask, with the lowest order bit corresponding to the first logical CPU

and the highest order bit corresponding to the last logical CPU. Not all CPUs may exist on a given system but

a mask may specify more CPUs than are present. A retrieved mask will reflect only the bits that correspond to

CPUs physically on the system. If an invalid mask is given (i.e., one that corresponds to no valid CPUs on the

current system) an error is returned. The masks are typically given in hexadecimal. For example,

0×00000001

is processor #0

0×00000003

is processors #0 and #1

0xFFFFFFFF

is all processors (#0 through #31)

When taskset returns, it is guaranteed that the given program has been scheduled to a legal CPU.

 

 

 

 

======================================================================

进程和线程的CPU亲和性

 

进程和线程的亲缘性(affinity)是指可以将进程或者是线程强制限制在可用的CPU子集上运行的特性,它一定程度上把进程/线程在多处理器系统上的调度策略暴露给系统程序员。
CPU的数量和表示在有n个CPU的Linux上,CPU是用0...n-1来进行一一标识的。CPU的数量可以通过proc文件系统下的CPU相关文件得到,如cpuinfo和stat:
$ cat /proc/stat | grep "^cpu[0-9]\+" | wc -l
8
$ cat /proc/cpuinfo | grep "^processor" | wc -l
8
在系统编程中,可以直接调用库调用sysconf获得:
sysconf(_SC_NPROCESSORS_ONLN);
进程的亲缘性Linux操作系统在2.5.8引入了调度亲缘性相关的系统调用:
int
sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);
int
sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);
其中sched_setaffinity是设定进程号为pid的进程调度亲缘性为mask,也就是说它只能在mask中指定的CPU之间进行调度执行;sched_getaffinity当然就是得到进程号为pid的进程调度亲缘性了。如果pid为0,则操纵当前进程。
第二个参数指定mask所指空间的大小,通常为sizeof(cpu_set_t)。
第三个参数mask的类型为cpu_set_t,即CPU集合,GNU的c库(需要在include头文件之前定义__USE_GNU)还提供了操作它们的宏:
void
CPU_CLR(int cpu, cpu_set_t *set);
int
CPU_ISSET(int cpu, cpu_set_t *set);
void
CPU_SET(int cpu, cpu_set_t *set);
void
CPU_ZERO(cpu_set_t *set);

如果我们所关心的只是CPU#0和CPU#1,想确保我们的进程只会运作在CPU#0之上,而不会运作在CPU#1之上。下面程序代码可以完成此事:

  1. cpu_set_t set;
  2. int ret, i;
  3. CPU_ZERO(&set);
  4. CPU_SET(0, &set);
  5. CPU_CLR(1, &set);
  6. ret = sched_setaffinity(0, sizeof(cpu_set_t), &set);
  7. if( ret == -1)
  8. {
  9. perror("sched_se");
  10. }
  11. for( i=0; i < 3; i++)
  12. {
  13. int cpu;
  14. cpu = CPU_ISSET(i, &set);
  15. printf("cpu = %i is %s/n", i,
  16. cpu? "set" :
    "unset");
  17. }
Linux只提供了面向线程的调度亲缘性一种接口,这也是上面只提调度亲缘性而不直言进程亲缘性的原因。当前Linux系统下广泛采用的线程库NPTL(Native Posix Thread Library)是基于线程组来实现的,同一个线程组中的线程对应于一组共享存储空间的轻量级进程,它们各自作为单独调度单位被内核的调度器在系统范围内调度,这种模型也就是我们通常所说的1-1线程模型。正因如此,目前线程的调度范围(可以用函数pthread_attr_getscope和pthread_attr_setscope获取和设置)只能是系统级而不能是进程级。
c库的GNU扩展所提供的有关线程亲缘性的API如下:
int pthread_attr_setaffinity_np (pthread_attr_t *__attr, size_t __cpusetsize, __const cpu_set_t *__cpuset);
int
pthread_attr_getaffinity_np (__const pthread_attr_t *__attr, size_t __cpusetsize, cpu_set_t *__cpuset);
int pthread_setaffinity_np (pthread_t __th, size_t __cpusetsize, __const cpu_set_t *__cpuset);
int
pthread_getaffinity_np (pthread_t __th, size_t __cpusetsize, cpu_set_t *__cpuset);
亲缘性的继承调度亲缘性是被fork出来的子进程所继承的,即使子进程通过exec系列函数更换了执行镜像。因为Linux操作系统下进程和线程的创建都是通过系统调用clone来实现的,所以实际上调度亲缘性也是被用pthread_create创建的线程所继承的。这意味着,如果主线程在创建其它线程之前设定亲缘性,那么它所设定的亲缘性将被继承,因为这时所有线程的亲缘性相同(假设之后没有任何线程私自设置亲缘性),我们就可以认为前面设置的是进程亲缘性,而不管它所调用的函数是sched_setaffinity还是pthread_setaffnity_np。
下面创建两个并发线程分别绑定在CPU0和CPU1上。
#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sys/time.h>
#include <math.h>
#include <time.h>
#include <sched.h>
#include <sys/types.h>
int x1;
int x2;
double waste_time(long n)
{
double res = 0;
long i = 0;
while (i <n * 500000) {
i++;
res += sqrt(i);
}
return res;
}
void* proc1(void*arg)
{
cpu_set_t mask ;
CPU_ZERO(&mask);
CPU_SET(0,&mask);
int ret = 0;

ret = pthread_setaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask );
if(ret < 0)
{
printf("pthread_setaffinity_np err \n");
return ;
}
while(1)
{
if(x1 > 900000000)
{
break;
}
x1++;
}
waste_time(1);
ret =pthread_getaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask );
if(ret < 0)
{
printf("pthread_getaffinity_np err \n");
return ;
}
int j;
for( j = 0;j < CPU_SETSIZE;j++)
{
if(CPU_ISSET(j,&mask))
printf(" thread[%d] bind cpu[%d]\n",pthread_self(),j);
}

}
void* proc2(void* arg)
{
cpu_set_t mask ;
CPU_ZERO(&mask);
CPU_SET(2,&mask);
int ret = 0;

ret =pthread_setaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask );
if(ret < 0)
{
printf("pthread_setaffinity_np err \n");
return ;
}

while(1)
{
if(x2 > 900000000)
{
break;
}
x2++;
}
waste_time(1);
ret =pthread_getaffinity_np(pthread_self(),sizeof(mask),(const cpu_set_t*)&mask );
if(ret < 0)
{
printf("pthread_getaffinity_np err \n");
return ;
}
int j;
for( j = 0;j < CPU_SETSIZE;j++)
{
if(CPU_ISSET(j,&mask))
printf(" thread[%d] bind cpu[%d]\n",pthread_self(),j);
}
}
void main()
{
int ret;
pthread_t t1,t2;
struct timeval time1,time2;

ret = gettimeofday(&time1,NULL);
ret = pthread_create(&t1,NULL,proc1,NULL);
ret = pthread_create(&t2,NULL,proc2,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
ret = gettimeofday(&time2,NULL);
printf("time spend:[%d]s [%d]ms \n",time2.tv_sec - time1.tv_sec,(time2.tv_usec - time1.tv_usec)/1000);

}

 

 

 

=========================================================================================

管理处理器的亲和性(affinity)

 

简介: 了解 Linux® 2.6 调度器如何处理 CPU 亲和性(affinity)可以帮助您更好地设计用户空间的应用程序。软亲和性(affinity) 意味着进程并不会在处理器之间频繁迁移,而
硬亲和性(affinity) 则意味着进程需要在您指定的处理器上运行。本文介绍了当前的亲和性(affinity)机制,解释为什么和如何使用亲和性(affinity),并给出了几个样例代码来显示如何使用这种功能。

 

简单地说,CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。Linux 内核进程调度器天生就具有被称为
软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。

2.6 版本的 Linux 内核还包含了一种机制,它让开发人员可以编程实现 硬 CPU 亲和性(affinity)。这意味着应用程序可以显式地指定进程在哪个(或哪些)处理器上运行。

什么是 Linux 内核硬亲和性(affinity)?

在 Linux 内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是
cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。

如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态。

Linux 内核 API 提供了一些方法,让用户可以修改位掩码或查看当前的位掩码:

  • sched_set_affinity() (用来修改位掩码)
  • sched_get_affinity() (用来查看当前的位掩码)

注意,cpu_affinity 会被传递给子线程,因此应该适当地调用 sched_set_affinity


为什么应该使用硬亲和性(affinity)?

通常 Linux 内核都可以很好地对进程进行调度,在应该运行的地方运行进程(这就是说,在可用的处理器上运行并获得很好的整体性能)。内核包含了一些用来检测 CPU 之间任务负载迁移的算法,可以启用进程迁移来降低繁忙的处理器的压力。

一般情况下,在应用程序中只需使用缺省的调度器行为。然而,您可能会希望修改这些缺省行为以实现性能的优化。让我们来看一下使用硬亲和性(affinity) 的 3 个原因。

原因 1. 有大量计算要做

基于大量计算的情形通常出现在科学和理论计算中,但是通用领域的计算也可能出现这种情况。一个常见的标志是您发现自己的应用程序要在多处理器的机器上花费大量的计算时间。

原因 2. 您在测试复杂的应用程序

测试复杂软件是我们对内核的亲和性(affinity)技术感兴趣的另外一个原因。考虑一个需要进行线性可伸缩性测试的应用程序。有些产品声明可以在 使用更多硬件 时执行得更好。

我们不用购买多台机器(为每种处理器配置都购买一台机器),而是可以:

  • 购买一台多处理器的机器
  • 不断增加分配的处理器
  • 测量每秒的事务数
  • 评估结果的可伸缩性

如果应用程序随着 CPU 的增加可以线性地伸缩,那么每秒事务数和 CPU 个数之间应该会是线性的关系(例如斜线图 —— 请参阅下一节的内容)。这样建模可以确定应用程序是否可以有效地使用底层硬件。

Amdahl 法则

Amdahl 法则是有关使用并行处理器来解决问题相对于只使用一个串行处理器来解决问题的加速比的法则。加速比(Speedup) 等于串行执行(只使用一个处理器)的时间除以程序并行执行(使用多个处理器)的时间:

      T(1)
S = ------
      T(j)
      

其中 T(j) 是在使用 j 个处理器执行程序时所花费的时间。

Amdahl 法则说明这种加速比在现实中可能并不会发生,但是可以非常接近于该值。对于通常情况来说,我们可以推论出每个程序都有一些串行的组件。随着问题集不断变大,串行组件最终会在优化解决方案时间方面达到一个上限。

Amdahl 法则在希望保持高 CPU 缓存命中率时尤其重要。如果一个给定的进程迁移到其他地方去了,那么它就失去了利用 CPU 缓存的优势。实际上,如果正在使用的 CPU 需要为自己缓存一些特殊的数据,那么所有其他 CPU 都会使这些数据在自己的缓存中失效。

因此,如果有多个线程都需要相同的数据,那么将这些线程绑定到一个特定的 CPU 上是非常有意义的,这样就确保它们可以访问相同的缓存数据(或者至少可以提高缓存的命中率)。否则,这些线程可能会在不同的 CPU 上执行,这样会频繁地使其他缓存项失效。

原因 3. 您正在运行时间敏感的、决定性的进程

我们对 CPU 亲和性(affinity)感兴趣的最后一个原因是实时(对时间敏感的)进程。例如,您可能会希望使用硬亲和性(affinity)来指定一个 8 路主机上的某个处理器,而同时允许其他 7 个处理器处理所有普通的系统调度。这种做法确保长时间运行、对时间敏感的应用程序可以得到运行,同时可以允许其他应用程序独占其余的计算资源。

下面的样例应用程序显示了这是如何工作的。


如何利用硬亲和性(affinity)

现在让我们来设计一个程序,它可以让 Linux 系统非常繁忙。可以使用前面介绍的系统调用和另外一些用来说明系统中有多少处理器的 API 来构建这个应用程序。实际上,我们的目标是编写这样一个程序:它可以让系统中的每个处理器都繁忙几秒钟。可以从后面的“下载”一节中
下载样例程序

清单 1. 让处理器繁忙

                
/* This method will create threads, then bind each to its own cpu. */
bool do_cpu_stress(int numthreads)
{
   int ret = TRUE;
   int created_thread = 0;
   /* We need a thread for each cpu we have... */
   while ( created_thread < numthreads - 1 )
   {
      int mypid = fork();
      if (mypid == 0) /* Child process */
       {
          printf("\tCreating Child Thread: #%i\n", created_thread);
          break;
      }
      else /* Only parent executes this */
      {
          /* Continue looping until we spawned enough threads! */ ;
          created_thread++;
      }
   }
   /* NOTE: All threads execute code from here down! */

正如您可以看到的一样,这段代码只是通过 fork 调用简单地创建一组线程。每个线程都执行这个方法中后面的代码。现在我们让每个线程都将亲和性(affinity)设置为自己的 CPU。

清单 2. 为每个线程设置 CPU 亲和性(affinity)

                
   cpu_set_t mask;
   /* CPU_ZERO initializes all the bits in the mask to zero. */
        CPU_ZERO( &mask );
   /* CPU_SET sets only the bit corresponding to cpu. */
        CPU_SET( created_thread, &mask );
   /* sched_setaffinity returns 0 in success */
        if( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 )
   {
      printf("WARNING: Could not set CPU Affinity, continuing...\n");
   }

如果程序可以执行到这儿,那么我们的线程就已经设置了自己的亲和性(affinity)。调用 sched_setaffinity 会设置由
pid 所引用的进程的 CPU 亲和性(affinity)掩码。如果 pid 为 0,那么就使用当前进程。

亲和性(affinity)掩码是使用在 mask 中存储的位掩码来表示的。最低位对应于系统中的第一个逻辑处理器,而最高位则对应于系统中最后一个逻辑处理器。

每个设置的位都对应一个可以合法调度的 CPU,而未设置的位则对应一个不可调度的 CPU。换而言之,进程都被绑定了,只能在那些对应位被设置了的处理器上运行。通常,掩码中的所有位都被置位了。这些线程的亲和性(affinity)都会传递给从它们派生的子进程中。

注意不应该直接修改位掩码。应该使用下面的宏。虽然在我们的例子中并没有全部使用这些宏,但是在本文中还是详细列出了这些宏,您在自己的程序中可能需要这些宏。

清单 3. 间接修改位掩码的宏

                
void CPU_ZERO (cpu_set_t *set)
这个宏对 CPU 集 set 进行初始化,将其设置为空集。
void CPU_SET (int cpu, cpu_set_t *set)
这个宏将 cpu 加入 CPU 集 set 中。
void CPU_CLR (int cpu, cpu_set_t *set)
这个宏将 cpu 从 CPU 集 set 中删除。
int CPU_ISSET (int cpu, const cpu_set_t *set)
如果 cpu 是 CPU 集 set 的一员,这个宏就返回一个非零值(true),否则就返回零(false)。

对于本文来说,样例代码会继续让每个线程都执行某些计算量较大的操作。

清单 4. 每个线程都执行一个计算敏感的操作

                
    /* Now we have a single thread bound to each cpu on the system */
    int computation_res = do_cpu_expensive_op(41);
    cpu_set_t mycpuid;
    sched_getaffinity(0, sizeof(mycpuid), &mycpuid);
    if ( check_cpu_expensive_op(computation_res) )
    {
      printf("SUCCESS: Thread completed, and PASSED integrity check!\n",
         mycpuid);
      ret = TRUE;
    }
    else
    {
      printf("FAILURE: Thread failed integrity check!\n",
         mycpuid);
      ret = FALSE;
    }
   return ret;
}

现在您已经了解了在 Linux 2.6 版本的内核中设置 CPU 亲和性(affinity)的基本知识。接下来,我们使用一个 main 程序来封装这些方法,它使用一个用户指定的参数来说明要让多少个 CPU 繁忙。我们可以使用另外一个方法来确定系统中有多少个处理器:

int NUM_PROCS = sysconf(_SC_NPROCESSORS_CONF);

这个方法让程序能够自己确定要让多少个处理器保持繁忙,例如缺省让所有的处理器都处于繁忙状态,并允许用户指定系统中实际处理器范围的一个子集。


运行样例程序

当运行前面介绍的
样例程序
时,可以使用很多工具来查看 CPU 是否是繁忙的。如果只是简单地进行测试,可以使用 Linux 命令 top。在运行
top
命令时按下 “1” 键,可以看到每个 CPU 执行进程所占用的百分比。


结束语

这个样例程序虽然非常简单,但是它却展示了使用 Linux 内核中实现的硬亲和性(affinity)的基本知识。(任何使用这段代码的应用程序都无疑会做一些更有意义的事情。)了解了 CPU 亲和性(affinity)内核 API 的基本知识,您就可以从复杂的应用程序中榨取出最后一点儿性能了。

 

抱歉!评论已关闭.