Unix/Linux编程:CPU亲和力

CPU亲和力

当一个进程在一个多处理器系统上被重新调度时无需在上一次执行的CPU上运行。之所以会在另一个CPU上运行的原因是原来的CPU处于忙碌状态。

进程切换CPU时对性能会有一定的影响:如果在原来的CPU的高速缓冲器中存在进程的数据,那么为了将进程的一行数据加载进新CPU的高速缓冲器中,首先必须使这行数据失效(即在没被修改的情况下丢失数据,在被修改的情况下将数据写入缓存)(为防止高速缓冲器不一致,多处理器架构在某个时刻只允许数据被存在在一个CPU的高速缓冲器中)。这个使数据失效的过程会消耗时间。这个使数据失效的过程会消耗时间。由于存在这个性能影响,Linux(2.6)内核尝试了给进程保证软 CPU 亲和力—在条件允许的情况下进程重新被调度到原来的CPU 上运行。

Linux 特有的/proc/PID/stat 文件中的一个字段显示了进程当前执行或上一次执行时所在的CPU 编号。具体请参见 proc(5)手册。

有时候需要为进程设置硬CPU亲和力,这样就能显式的将其限制在可用CPU中的一个或者一组CPU上运行。之所以需要这样做,原因如下:

  • 可以避免由使高速缓冲器中的数据失效所带来的性能影响
  • 如果多个线程(或者进程)访问同样的数据,那么当将它们限制在同样的CPU上的化可能会带来性能提升,因为它们无需竞争数据并且也不存在由此而产生的高速缓冲器未命中
  • 对于时间关键的应用程序来讲,可能需要为此应用程序预留一个或者更多CPU,而将系统中大多数进程限制在其他CPU上
  • 使用 isolcpus 内核启动参数能够将一个或更多 CPU 分离出常规的内核调度算法。将一个进程移到或者移除被分离的CPU的唯一方式就是使用CPU亲和力系统调用。。isolcpus启动参数是实现上面列出的最后一种场景的首选方式,具体可参考内核源文件 Documentation/ kernel-parameters.txt
  • Linux 还提供了一个 cpuset 内核参数,该参数可用于包含大量 CPU 的系统以实现如何给进程分配 CPU 和内存的复杂控制,具体可参考内核源文件 Documentation/cpusets.txt

Linux 2.6 提供了一对非标准的系统调用来修改和获取进程的硬 CPU 亲和力:sched_ setaffinity()和sched_getaffinity()。


NAME
       sched_setaffinity, sched_getaffinity - set and get a process's CPU affinity mask

SYNOPSIS
       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <sched.h>

       int sched_setaffinity(pid_t pid, size_t cpusetsize,
                             cpu_set_t *mask);

       int sched_getaffinity(pid_t pid, size_t cpusetsize,
                             cpu_set_t *mask);

sched_setaffinity()系统调用设置了 pid 指定的进程的 CPU 亲和力。如果 pid 为 0,那么调用进程的 CPU 亲和力就会被改变。赋给进程的 CPU 亲和力由 set 指向的 cpu_set_t 结构来指定。

虽然 cpu_set_t 数据类型实现为一个位掩码,但应该将其看成是一个不透明的结构。所有对这个结构的操作都应该使用宏 CPU_ZERO()、CPU_SET()、CPU_CLR()和 CPU_ISSET()等来完成

SYNOPSIS
       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <sched.h>

       void CPU_ZERO(cpu_set_t *set);

       void CPU_SET(int cpu, cpu_set_t *set);
       void CPU_CLR(int cpu, cpu_set_t *set);
       int  CPU_ISSET(int cpu, cpu_set_t *set);

       int  CPU_COUNT(cpu_set_t *set);

       void CPU_AND(cpu_set_t *destset,
                    cpu_set_t *srcset1, cpu_set_t *srcset2);
		......

下面这些宏操作 set 指向的 CPU 集合:

  • CPU_ZERO()将 set 初始化为空。
  • CPU_SET()将 CPU cpu 添加到 set 中。
  • CPU_CLR()从 set 中删除 CPU cpu。
  • CPU_ISSET()在 CPU cpu 是 set 的一个成员时返回 true。

CPU集合中的CPU从0开始编号。<sched.h>头文件定义了常量CPU_SETSIZE,它是比cpu_set_t变量能够表示的最大CPU编号还要大的一个数字。CPU_SETSIZE的值为 1024。

传递给 sched_setaffinity()的 len 参数应该指定 set 参数中字节数(即 sizeof(cpu_set_t))。

下面的代码将 pid 标识出的进程限制在四处理器系统上除第一个 CPU 之外的任意 CPU 上运行:

cpu_set_t set;

CPU_ZERO(&set);
CPU_SET(1, &set);
CPU_SET(2, &set);
CPU_SET(3, &set);

sched_setaffinity(pid, CPU_SETSIZE,&set);

如果 set 中指定的 CPU 与系统中的所有 CPU 都不匹配,那么 sched_setaffinity()调用就会返回 EINVAL 错误。

如果运行调用进程的CPU不包含在set中,那么进程会被迁移到set中的一个CPU上。

非特权进程只有在其有效用户 ID 与目标进程的真实或有效用户 ID 匹配时才能够设置目标进程的 CPU 亲和力。特权(CAP_SYS_NICE)进程可以设置任意进程的 CPU 亲和力。

sched_getaffinity()系统调用获取 pid 指定的进程的 CPU 亲和力掩码。如果 pid 为 0,那么就返回调用进程的 CPU 亲和力掩码

  • 返回的 CPU 亲和力掩码位于 set 指向的 cpu_set_t 结构中,同时应该将 len 参数设置为结构中包含的字节数,即 sizeof(cpu_set_t)。使用 CPU_ISSET()宏能够确定哪些 CPU 位于 set中
  • 如果目标进程的 CPU 亲和力掩码并没有被修改过,那么 sched_getaffinity()返回包含系统中所有 CPU 的集合。
  • sched_getaffinity()执行时不会进行权限检查,非特权进程能够获取系统上所有进程的 CPU亲和力掩码

通过fork()创建的子进程会继承其父进程的CPU亲和力掩码并且在exec()调用之间掩码会得以保留。

sched_setaffinity()和 sched_getaffinity()系统调用是 Linux 特有的

总结

进程的CPU亲和力掩码可以用来将进程限制在多处理器系统上可用CPU的子集中运行。这样就可以提高特定类型的应用程序的性能

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值