0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

把进程绑定到某个 CPU 上运行是怎么实现?

Linux爱好者 来源:Linux内核那些事 作者:songsong001 2021-07-02 09:55 次阅读

昨天在群里有朋友问:把进程绑定到某个 CPU 上运行是怎么实现的。

首先,我们先来了解下将进程与 CPU 进行绑定的好处。

进程绑定 CPU 的好处:在多核 CPU 结构中,每个核心有各自的L1、L2缓存,而L3缓存是共用的。如果一个进程在核心间来回切换,各个核心的缓存命中率就会受到影响。相反如果进程不管如何调度,都始终可以在一个核心上执行,那么其数据的L1、L2 缓存的命中率可以显著提高。

所以,将进程与 CPU 进行绑定可以提高 CPU 缓存的命中率,从而提高性能。而进程与 CPU 绑定被称为:CPU 亲和性。

设置进程的 CPU 亲和性前面介绍了进程与 CPU 绑定的好处后,现在来介绍一下在 Linux 系统下怎么将进程与 CPU 进行绑定的(也就是设置进程的 CPU 亲和性)。

Linux 系统提供了一个名为 sched_setaffinity 的系统调用,此系统调用可以设置进程的 CPU 亲和性。我们来看看 sched_setaffinity 系统调用的原型:

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

下面介绍一下 sched_setaffinity 系统调用各个参数的作用:

pid:进程ID,也就是要进行绑定 CPU 的进程ID。

cpusetsize:mask 参数所指向的 CPU 集合的大小。

mask:与进程进行绑定的 CPU 集合(由于一个进程可以绑定到多个 CPU 上运行)。

参数 mask 的类型为 cpu_set_t,而 cpu_set_t 是一个位图,位图的每个位表示一个 CPU。:

例如,将 cpu_set_t 的第0位设置为1,表示将进程绑定到 CPU0 上运行,当然我们可以将进程绑定到多个 CPU 上运行。

我们通过一个例子来介绍怎么通过 sched_setaffinity 系统调用来设置进程的 CPU 亲和性:

#define _GNU_SOURCE#include 《sched.h》#include 《stdio.h》#include 《string.h》#include 《stdlib.h》#include 《unistd.h》#include 《errno.h》int main(int argc, char **argv)

{

cpu_set_t cpuset;

CPU_ZERO(&cpuset); // 初始化CPU集合,将 cpuset 置为空

CPU_SET(2, &cpuset); // 将本进程绑定到 CPU2 上

// 设置进程的 CPU 亲和性

if (sched_setaffinity(0, sizeof(cpuset), &cpuset) == -1) {

printf(“Set CPU affinity failed, error: %s

”, strerror(errno));

return -1;

}

return 0;

}

CPU 亲和性实现知道怎么设置进程的 CPU 亲和性后,现在我们来分析一下 Linux 内核是怎样实现 CPU 亲和性功能的。

本文使用的 Linux 内核版本为 2.6.23

Linux 内核为每个 CPU 定义了一个类型为 struct rq 的 可运行的进程队列,也就是说,每个 CPU 都拥有一个独立的可运行进程队列。

一般来说,CPU 只会从属于自己的可运行进程队列中选择一个进程来运行。也就是说,CPU0 只会从属于 CPU0 的可运行队列中选择一个进程来运行,而绝不会从 CPU1 的可运行队列中获取。

所以,从上面的信息中可以分析出,要将进程绑定到某个 CPU 上运行,只需要将进程放置到其所属的 可运行进程队列 中即可。

下面我们来分析一下 sched_setaffinity 系统调用的实现,sched_setaffinity 系统调用的调用链如下:

sys_sched_setaffinity()

└→ sched_setaffinity()

└→ set_cpus_allowed()

└→ migrate_task()

从上面的调用链可以看出,sched_setaffinity 系统调用最终会调用 migrate_task 函数来完成进程与 CPU 进行绑定的工作,我们来分析一下 migrate_task 函数的实现:

static int

migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req)

{

struct rq *rq = task_rq(p);

// 情况1:

// 如果进程还没有在任何运行队列中

// 那么只需要将进程的 cpu 字段设置为 dest_cpu 即可

if (!p-》se.on_rq && !task_running(rq, p)) {

set_task_cpu(p, dest_cpu);

return 0;

}

// 情况2:

// 如果进程已经在某一个 CPU 的可运行队列中

// 那么需要将进程从之前的 CPU 可运行队列中迁移到新的 CPU 可运行队列中

// 这个迁移过程由 migration_thread 内核线程完成

// 构建进程迁移请求

init_completion(&req-》done);

req-》task = p;

req-》dest_cpu = dest_cpu;

list_add(&req-》list, &rq-》migration_queue);

return 1;

}

我们先来介绍一下 migrate_task 函数各个参数的意义:

p:要设置 CPU 亲和性的进程描述符。

dest_cpu:绑定的 CPU 编号。

req:进程迁移请求对象(下面会介绍)。

所以,migrate_task 函数的作用就是将进程描述符为 p 的进程绑定到编号为 dest_cpu 的目标 CPU 上。

migrate_task 函数主要分两种情况来将进程绑定到某个 CPU 上:

情况1:如果进程还没有在任何 CPU 的可运行队列中(不可运行状态),那么只需要将进程描述符的 cpu 字段设置为 dest_cpu 即可。当进程变为可运行时,会根据进程描述符的 cpu 字段来自动放置到对应的 CPU 可运行队列中。

情况2:如果进程已经在某个 CPU 的可运行队列中,那么需要将进程从之前的 CPU 可运行队列中迁移到新的 CPU 可运行队列中。迁移过程由 migration_thread 内核线程完成,migrate_task 函数只是构建一个进程迁移请求,并通知 migration_thread 内核线程有新的迁移请求需要处理。

而进程迁移过程由 __migrate_task 函数完成,我们来看看 __migrate_task 函数的实现:

static int

__migrate_task(struct task_struct *p, int src_cpu, int dest_cpu)

{

struct rq *rq_dest, *rq_src;

int ret = 0, on_rq;

。。。

rq_src = cpu_rq(src_cpu); // 进程所在的原可运行队列

rq_dest = cpu_rq(dest_cpu); // 进程希望放置的目标可运行队列

。。。

on_rq = p-》se.on_rq; // 进程是否在可运行队列中(可运行状态)

if (on_rq)

deactivate_task(rq_src, p, 0); // 把进程从原来的可运行队列中删除

set_task_cpu(p, dest_cpu);

if (on_rq) {

activate_task(rq_dest, p, 0); // 把进程放置到目标可运行队列中

。。。

}

。。。

return ret;

}

__migrate_task 函数主要完成以下两个工作:

把进程从原来的可运行队列中删除。

把进程放置到目标可运行队列中。

其工作过程如下图所示(将进程从 CPU0 的可运行队列迁移到 CPU3 的可运行队列中):

如上图所示,进程原本在 CPU0 的可运行队列中,但由于重新将进程绑定到 CPU3,所以需要将进程从 CPU0 的可运行队列迁移到 CPU3 的可运行中。

迁移过程首先将进程从 CPU0 的可运行队列中删除,然后再将进程插入到 CPU3 的可运行队列中。

当 CPU 要运行进程时,首先从它所属的可运行队列中挑选一个进程,并将此进程调度到 CPU 中运行。

总结从上面的分析可知,其实将进程绑定到某个 CPU 只是将进程放置到 CPU 的可运行队列中。

由于每个 CPU 都有一个可运行队列,所以就有可能会出现 CPU 间可运行队列负载不均衡问题。如 CPU0 可运行队列中的进程比 CPU1 可运行队列多非常多,从而导致 CPU0 的负载非常高,而 CPU1 负载非常低的情况。

当出现上述情况时,就需要对 CPU 间的可运行队列进行重平衡操作,有兴趣的可以自行阅读源码或参考相关资料

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内核
    +关注

    关注

    3

    文章

    1309

    浏览量

    39843
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10436

    浏览量

    206523
  • Linux
    +关注

    关注

    87

    文章

    10987

    浏览量

    206723

原文标题:图解:进程怎么绑定 CPU

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    鸿蒙OS跨进程IPC与RPC通信

    一、IPC与RPC通信概述 基本概念 IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现进程通信,不同的是前者
    发表于 02-17 14:20

    请问tc233 Uart收发数据如何绑定DMA?

    请问tc233 Uart收发数据如何绑定DMA?绑定DMA之后中断触发还是原来的 IfxCpu_Irq_installInterruptHandler()这个函数里
    发表于 01-22 06:27

    Linux内核驱动与单个PCI设备的绑定和解绑定

    在Linux内核2.6.13-rc3以前,驱动和设备之间的绑定和解绑只能通过insmod(modprobe)和rmmod来实现,但是这种实现方法有一个弊端,就是一旦绑定或者解
    的头像 发表于 11-17 17:11 825次阅读
    Linux内核驱动与单个PCI设备的<b class='flag-5'>绑定</b>和解<b class='flag-5'>绑定</b>

    什么是进程

    组织形式的描述,进程是程序的实体。进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当
    的头像 发表于 11-08 15:21 1928次阅读
    什么是<b class='flag-5'>进程</b>

    labview 数据绑定求助

    求助 LABVIEW 大神 在每个控件属性中有一个数据绑定绑定本机的OPC服务器 是正常可以用的如果我想把这个绑定路径做成根我提供的
    发表于 10-24 09:19

    PCB进程的创建和终止过程

    PCB进程控制块 独立运行基本单位的标志:创建进程时创建PCB,进程结束时回PCB,进程随之消亡。系统是通过PCB,感知
    的头像 发表于 10-08 15:36 775次阅读

    HarmonyOS/OpenHarmony(Stage模型)应用开发手势绑定方法

    (gesture: GestureType, mask?: GestureMask) gesture为通用的一种手势绑定方法,可以将手势绑定对应的组件。 例如,可以将点击手势T
    发表于 08-29 15:24

    运行多个进程时,OpenVINO trade推断时间会增加是为什么?如何解决?

    通过运行两个进程来推断同一个模型,推断时间翻倍。 使用OpenVINO™推断模型。 运行单个过程时,推断时间约为 300ms。 使用两个进程时,两个
    发表于 08-15 07:04

    进程和线程的区别以及优缺点

    在执行,多核 CPU 可以同一时间点有多个进程在执行。 2、多进程、多线程的优缺点 一个进程进程控制块、数据段、代码段组成,
    的头像 发表于 07-21 11:02 841次阅读

    [译]在RISC-V CPU运行OpenCV

    的跨平台API和平台特定的实现(使用本机内部函数,例如x64平台的SSE/AVX内部函数,ARM的NEON内部函数等)。您可以在core模块中找到相应的实现:https://git
    发表于 06-22 18:54

    文盘Rust -- tokio绑定cpu实践

    要想把负载绑定在某一 core 上,需要使用 core_affinity_rs(https://github.com/Elzair/core_affinity_rs
    的头像 发表于 06-11 15:32 370次阅读
    文盘Rust -- tokio<b class='flag-5'>绑定</b><b class='flag-5'>cpu</b>实践

    Linux进程的睡眠和唤醒

    在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状 态标志位为 TASK_RUNNING。一旦
    发表于 06-07 12:26 279次阅读

    CPU的硬件运行效率

    提到CPU性能,大部分同学想到的都是CPU利用率,这个指标确实应该首先被关注。但是除了利用率之外,还有很容易被人忽视的指标,就是指令的运行效率。如果运行效率不高,那
    的头像 发表于 05-17 10:48 683次阅读
    <b class='flag-5'>CPU</b>的硬件<b class='flag-5'>运行</b>效率

    gstreamer采用waylandsink并绑定QWidget失败?

      我现在在RK3568平台下想基于QT+Gstreamer开发媒体播放器,   gstreamer部分我采用了waylandsink,想将Gstreamer的播放绑定QT的一个QWidget窗口
    发表于 05-11 16:01

    Linux内核是如何限制进程CPU资源使用的?

    在使用 Kubernetes(简称K8s) 时,通常会在同一台机器上部署多个 Pod。如果某个 Pod 中的服务出现问题(如出现死循环),将会导致占用大量的 CPU 时间,从而影响到其他 Pod 的正常运行
    发表于 05-08 18:14 877次阅读
    Linux内核是如何限制<b class='flag-5'>进程</b>对<b class='flag-5'>CPU</b>资源使用的?