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

    文章

    1372

    浏览量

    40282
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10855

    浏览量

    211601
  • Linux
    +关注

    关注

    87

    文章

    11295

    浏览量

    209348

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

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

收藏 人收藏

    评论

    相关推荐

    深入解析Linux程序与进程

    关于某个数据集合的一次运行活动。作为系统进行资源分配和调度的基本单位,是操作系统结构的基础。 程序与进程的关系 进程的状态 基础进程状态 创
    的头像 发表于 12-18 11:01 70次阅读
    深入解析Linux程序与<b class='flag-5'>进程</b>

    Linux之CPU调度策略和CPU亲和性

    一、调度策略 调度进程 单个 CPU一次只能执行一个进程,虽然 Linux 系统通过使用多任务同时处理多个进程,但当多个进程同时
    的头像 发表于 12-05 16:38 389次阅读
    Linux之<b class='flag-5'>CPU</b>调度策略和<b class='flag-5'>CPU</b>亲和性

    fpga 管脚不让绑定的问题,绑定时提示: Not assignable

    fpga 管脚不让绑定的--提示 如下图: 网上说将复用管脚设置成 普通I/O,我这也没找到我要绑定的管脚,怎么设置。该管脚是和NOR_Flash相关的,无法绑定,我想实现掉电存储一些
    发表于 12-05 15:30

    怎么绑定IP地址和MAC地址

    本篇文章主要说一说怎么在本地计算机上绑定IP和MAC地址以及通过路由器绑定IP和MAC地址,并且这两者绑定可以解决什么问题。   先来说说怎么去绑定 本地计算上
    的头像 发表于 12-01 22:48 211次阅读

    一文搞懂Linux进程的睡眠和唤醒

    的代码和数据,进而去执行这个进程。下面列举了一些进程状态: 注意:没有+时,默认是后台进程 进程调度(进程状态切换)
    发表于 11-04 15:15

    【软件干货】Android应用进程如何保活?

    在Android应用程序中,为了保证应用的正常运行和稳定性,有时需要对应用进程进行保活。以下是一些实现进程保活的方法:
    的头像 发表于 10-15 17:05 406次阅读
    【软件干货】Android应用<b class='flag-5'>进程</b>如何保活?

    rk3588s 怎么将gpio中断绑定其它cpu

    目前了解只有GIC的中断才能用这种:echo 2 > /proc/irq/102/smp_affinity, 方式绑定cpu,而GPIO这种是用不了这种方式绑定的。 想问下有其
    发表于 07-16 15:09

    nginx重启命令linux步骤是什么?

    worker进程,不可能处理其它进程的请求。   worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致。同时,nginx为了更好的利用多核特性,具有
    发表于 07-11 17:13

    nginx重启命令linux步骤是什么?

    worker进程,不可能处理其它进程的请求。   worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致。同时,nginx为了更好的利用多核特性,具有
    发表于 07-10 16:40

    ESP32S3的差分OTA升级,有没有方法可以用户固定某个位置,或者用户程序放到最后?

    我想做一个ESP32 S3 的差分OTA升级,但发现ESP-IDF编译生成的BIN文件用户程序放在前面,ESP32的库文件放在后面。这样即使改动很小的做出的差分程序也很大。有没有方法可以用户固定
    发表于 06-07 07:18

    CPU渲染和GPU渲染优劣分析

    GPU的出现,基于GPU的渲染获得了很大的普及。这些GPU是特定用途的芯片,在某些情况下提供与CPU渲染相当的结果。从广义讲,GPU渲染允许同时运行更多的并行进程
    的头像 发表于 05-23 08:27 591次阅读
    <b class='flag-5'>CPU</b>渲染和GPU渲染优劣分析

    STM32F4的裸机源码可以移植linux ARM运行吗? 具体需要怎么实现呢?

    STM32F4的裸机源码可以移植linux ARM开发板运行吗? 具体需要怎么实现呢?
    发表于 03-20 07:00

    鸿蒙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

    线程、进程、多线程、多进程和多任务之间有何关系?

    进程是程序执行时的一个实例,即它是程序已经执行课中程度的数据结构的汇集。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。
    的头像 发表于 01-11 13:39 358次阅读
    线程、<b class='flag-5'>进程</b>、多线程、多<b class='flag-5'>进程</b>和多任务之间有何关系?