多核优化,使用linux affinity 将进程,线程,中断指定到对应的cpu运行,用ftrace查看消耗时间

来源:互联网 发布:康得新 财务 数据报表 编辑:程序博客网 时间:2024/06/09 21:08
cpu的affinity简介
使用cpu的affinity机制可以将对应的进程,线程,以及中断指定代对应的cpu上运行,如果合理配置,减少某个cpu负担,提高其他cpu的使用率,从而到达提高系统性能的效果.

相关监控命令


查看cpu状态,及设置cpu开关及其频率 位置:

$ ls /sys/devices/system/cpu/
cpu0 cpu2 cpufreq offline possible
cpu1 cpu3 kernel_max online present

查看进程执行所对应的cpu号

$top -d 1 #然后按f 选择j (含有SMP选项)

如下:





使用linux affinity 将进程,线程,中断指定到对应的cpu运行

一. 进程
1.使用taskset设置执行文件对应的 CPU或者cp组


taskset搜索并设定运行进程的CPU亲和性(根据进程ID)。它还可用于启动给定CPU亲和性的进程,这样就可将指定的进程与指定的CPU或者一组CPU捆绑

$ taskset mask -- program

该值所表示的cup号所对应的位值:
CPU0 ---------- 00000001
CPU1 ---------- 00000010
CPU2 ---------- 00000100
(ps:当为3时则表示使用CPU0 和CPU1)
与其将处理器指定为位码,您还可以使用 -c 选项提供逗号分开的独立处理器,或者一组处理器列表,类似如下: 

$ taskset -c 0,5,7-9 -- myprogram

有关 taskset 的详情请参考 man page:man taskset。 
这里要注意的是我们可以把某个程序限定在某一些CPU上运行,但这并不意味着该程序可以独占这些CPU,其实其他程序还是可以利用这些CPU运行。如果要精确控制CPU,taskset就略嫌不足,cpuset才是可以的。

二.线程
在c程序中使用以下两个函数
  • sched_set_affinity() (用来修改位掩码)
  • sched_get_affinity() (用来查看当前的位掩码)
注意,cpu_affinity 会被传递给子线程,因此应该适当地调用 sched_set_affinity
一个例子:
#include<stdlib.h>  
#include<stdio.h>  
#include<sys/types.h>  
#include<sys/sysinfo.h>  
#include<unistd.h>  
   
#define __USE_GNU  
#include<sched.h>  
#include<ctype.h>  
#include<string.h>  
#include<pthread.h>  
#define THREAD_MAX_NUM 100  //1个CPU内的最多进程数  
   
int num=0;  //cpu中核数  
void* threadFun(void* arg)  //arg  传递线程标号(自己定义)  
{  
         cpu_set_t mask;  //CPU核的集合  
         cpu_set_t get;   //获取在集合中的CPU  
         int *a = (int *)arg;   
         printf("the a is:%d\n",*a);  //显示是第几个线程  
         CPU_ZERO(&mask);    //置空  
         CPU_SET(*a,&mask);   //设置亲和力值  
         if (sched_setaffinity(0, sizeof(mask), &mask) == -1)//设置线程CPU亲和力  
         {  
                   printf("warning: could not set CPU affinity, continuing...\n");  
         }  
         while (1)  
         {  
                   CPU_ZERO(&get);  
                   if (sched_getaffinity(0, sizeof(get), &get) == -1)//获取线程CPU亲和力  
                   {  
                            printf("warning: cound not get thread affinity, continuing...\n");  
                   }  
                   int i;  
                   for (i = 0; i < num; i++)  
                   {  
                            if (CPU_ISSET(i, &get))//判断线程与哪个CPU有亲和力  
                            {  
                                     printf("this thread %d is running processor : %d\n", i,i);  
                            }  
                   }  
         }  
   
         return NULL;  
}  
   
int main(int argc, char* argv[])  
{  
         num = sysconf(_SC_NPROCESSORS_CONF);  //获取核数  
         pthread_t thread[THREAD_MAX_NUM];  
         printf("system has %i processor(s). \n", num);  
         int tid[THREAD_MAX_NUM];  
         int i;  
         for(i=0;i<num;i++)  
         {  
                   tid[i] = i;  //每个线程必须有个tid[i]  
                   pthread_create(&thread[0],NULL,threadFun,(void*)&tid[i]);  
         }  
         for(i=0; i< num; i++)  
         {  
                   pthread_join(thread[i],NULL);//等待所有的线程结束,线程为死循环所以CTRL+C结束  
         }  
         return 0;  
三. 中断
当中断频繁发生时,进行上下文切换时,会消耗cpu很多资源,如果使用多核cpu芯片平台,可以考虑把中断处理交给不同cpu处理.

例如:要为四核cpu指定以太网驱动程序,首先要确定与该以太网驱动程序关联的 IRQ 数: 
# cat /proc/interrupts | grep eth0
32:   0     140      45       850264      PCI-MSI-edge      eth0使用 IRQ 数定位正确的 smp_affinity 文件: 
在手动绑定 IRQ 到 CPU 之前需要先停掉 irqbalance 这个服务,irqbalance 是个服务进程、是用来自动绑定和平衡 IRQ 的:
$ /etc/init.d/irqbalance stop

$ cat /proc/irq/32/smp_affinity 
fsmp_affinity 的默认值为 f,即可为系统中任意 CPU 提供 IRQ。将这个值设定为 1,如下,即表示只有 CPU 0 可以提供这个中断: 
$ echo 1 >/proc/irq/32/smp_affinity
$ cat /proc/irq/32/smp_affinity
1



对于频繁磁盘控制中断和网卡控制中断可以将其绑定在不同的单个cpu上这样可以有效的提高系统的响应时间,到达优化性能的效果。当然对于不同的中断,需要另外分析。


-----------------------------------------------------------------------------------------------
使用ftrace 调试cpu内核函数调用所消耗时间,
ftrace 的数据文件
/sys/kernel/debug/trace 目录下文件和目录比较多,有些是各种跟踪器共享使用的,有些是特定于某个跟踪器使用的。在操作这些数据文件时,通常使用 echo 命令来修改其值,也可以在程序中通过文件读写相关的函数来操作这些文件的值。下面只对部分文件进行描述,读者可以参考内核源码包中 Documentation/trace 目录下的文档以及 kernel/trace 下的源文件以了解其余文件的用途。
  • README文件提供了一个简短的使用说明,展示了 ftrace 的操作命令序列。可以通过 cat 命令查看该文件以了解概要的操作流程。
  • current_tracer用于设置或显示当前使用的跟踪器;使用 echo 将跟踪器名字写入该文件可以切换到不同的跟踪器。系统启动后,其缺省值为 nop ,即不做任何跟踪操作。在执行完一段跟踪任务后,可以通过向该文件写入 nop 来重置跟踪器。
  • available_tracers记录了当前编译进内核的跟踪器的列表,可以通过 cat 查看其内容;其包含的跟踪器与图 3 中所激活的选项是对应的。写 current_tracer 文件时用到的跟踪器名字必须在该文件列出的跟踪器名字列表中。
  • trace文件提供了查看获取到的跟踪信息的接口。可以通过 cat 等命令查看该文件以查看跟踪到的内核活动记录,也可以将其内容保存为记录文件以备后续查看。
  • tracing_enabled用于控制 current_tracer 中的跟踪器是否可以跟踪内核函数的调用情况。写入 0 会关闭跟踪活动,写入 1 则激活跟踪功能;其缺省值为 1 。
  • set_graph_function设置要清晰显示调用关系的函数,显示的信息结构类似于 C 语言代码,这样在分析内核运作流程时会更加直观一些。在使用 function_graph 跟踪器时使用;缺省为对所有函数都生成调用关系序列,可以通过写该文件来指定需要特别关注的函数。
  • buffer_size_kb用于设置单个 CPU 所使用的跟踪缓存的大小。跟踪器会将跟踪到的信息写入缓存,每个 CPU 的跟踪缓存是一样大的。跟踪缓存实现为环形缓冲区的形式,如果跟踪到的信息太多,则旧的信息会被新的跟踪信息覆盖掉。注意,要更改该文件的值需要先将 current_tracer 设置为 nop 才可以。
  • tracing_on用于控制跟踪的暂停。有时候在观察到某些事件时想暂时关闭跟踪,可以将 0 写入该文件以停止跟踪,这样跟踪缓冲区中比较新的部分是与所关注的事件相关的;写入 1 可以继续跟踪。
  • available_filter_functions记录了当前可以跟踪的内核函数。对于不在该文件中列出的函数,无法跟踪其活动。
  • set_ftrace_filter和 set_ftrace_notrace在编译内核时配置了动态 ftrace (选中 CONFIG_DYNAMIC_FTRACE 选项)后使用。前者用于显示指定要跟踪的函数,后者则作用相反,用于指定不跟踪的函数。如果一个函数名同时出现在这两个文件中,则这个函数的执行状况不会被跟踪。这些文件还支持简单形式的含有通配符的表达式,这样可以用一个表达式一次指定多个目标函数;具体使用在后续文章中会有描述。注意,要写入这两个文件的函数名必须可以在文件 available_filter_functions 中看到。缺省为可以跟踪所有内核函数,文件 set_ftrace_notrace 的值则为空。
ftrace 跟踪器
ftrace 当前包含多个跟踪器,用于跟踪不同类型的信息,比如进程调度、中断关闭等。可以查看文件 available_tracers 获取内核当前支持的跟踪器列表。在编译内核时,也可以看到内核支持的跟踪器对应的选项,如之前图 3 所示。
  • nop跟踪器不会跟踪任何内核活动,将 nop 写入 current_tracer 文件可以删除之前所使用的跟踪器,并清空之前收集到的跟踪信息,即刷新 trace 文件。
  • function跟踪器可以跟踪内核函数的执行情况;可以通过文件 set_ftrace_filter 显示指定要跟踪的函数。
  • function_graph跟踪器可以显示类似 C 源码的函数调用关系图,这样查看起来比较直观一些;可以通过文件 set_grapch_function 显示指定要生成调用流程图的函数。
  • sched_switch跟踪器可以对内核中的进程调度活动进行跟踪。
  • irqsoff跟踪器和 preemptoff跟踪器分别跟踪关闭中断的代码和禁止进程抢占的代码,并记录关闭的最大时长,preemptirqsoff跟踪器则可以看做它们的组合。
ftrace 还支持其它一些跟踪器,比如 initcall、ksym_tracer、mmiotrace、sysprof 等。ftrace 框架支持扩展添加新的跟踪器。读者可以参考内核源码包中 Documentation/trace 目录下的文档以及 kernel/trace 下的源文件,以了解其它跟踪器的用途和如何添加新的跟踪器。

下面单独介绍下
sched_switch 跟踪器
sched_switch 跟踪器可以对进程的调度切换以及之间的唤醒操作进行跟踪,如下所示。

sched_switch 跟踪器使用示例
[root@linux tracing]# pwd /sys/kernel/debug/tracing
[root@linux tracing]# echo 0 > tracing_enabled
[root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled
[root@linux tracing]# echo sched_switch > current_tracer
[root@linux tracing]# echo 1 > tracing_on
[root@linux tracing]# echo 1 > tracing_enabled

 # 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪

[root@linux tracing]# echo 0 > tracing_enabled
[root@linux tracing]# cat trace | head -10
 
 # tracer: sched_switch
 #
 #  TASK-PID    CPU#    TIMESTAMP  FUNCTION
 #     | |       |          |         |
      bash-1408  [000] 26208.816058:   1408:120:S   + [000]  1408:120:S bash
      bash-1408  [000] 26208.816070:   1408:120:S   + [000]  1408:120:S bash
      bash-1408  [000] 26208.816921:   1408:120:R   + [000]     9:120:R events/0
      bash-1408  [000] 26208.816939:   1408:120:R ==> [000]     9:120:R events/0
  events/0-9     [000] 26208.817081:      9:120:R   + [000]  1377:120:R gnome-terminal
  events/0-9     [000] 26208.817088:      9:120:S ==> [000]  1377:120:R gnome-terminal

在 sched_swich 跟踪器获取的跟踪信息中记录了进程间的唤醒操作和调度切换信息,可以通过符号‘ + ’和‘ ==> ’区分;唤醒操作记录给出了当前进程唤醒运行的进程,进程调度切换记录中显示了接替当前进程运行的后续进程。
描述进程状态的格式为“Task-PID:Priority:Task-State”。以示例跟踪信息中的第一条跟踪记录为例,可以看到进程 bash 的 PID 为 1408 ,其对应的内核态优先级为 120 ,当前状态为 S(可中断睡眠状态),当前 bash 并没有唤醒其它进程;从第 3 条记录可以看到,进程 bash 将进程 events/0 唤醒,而在第 4 条记录中发生了进程调度,进程 bash 切换到进程 events/0 执行。
在 Linux 内核中,进程的状态在内核头文件 include/linux/sched.h 中定义,包括可运行状态 TASK_RUNNING(对应跟踪信息中的符号‘ R ’)、可中断阻塞状态 TASK_INTERRUPTIBLE(对应跟踪信息中的符号‘ S ’)等。同时该头文件也定义了用户态进程所使用的优先级的范围,最小值为 MAX_USER_RT_PRIO(值为 100 ),最大值为 MAX_PRIO - 1(对应值为 139 ),缺省为 DEFAULT_PRIO(值为 120 );在本例中,进程优先级都是缺省值 120 。
ftrace还有许多跟踪器提供,可以在不同场合选择相应的调试器。
参考:
https://www.ibm.com/developerworks/cn/linux/l-affinity.html
http://blog.csdn.net/lanyzh0909/article/details/50404664
https://my.oschina.net/sharelinux/blog/146858
https://www.ibm.com/developerworks/cn/linux/l-cn-ftrace1/index.html
https://www.ibm.com/developerworks/cn/linux/l-cn-ftrace2/index.html


阅读全文
0 0
原创粉丝点击