b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

Linux系统下怎么将进程与CPU绑定到某个CPU

电脑杂谈  发布时间:2021-05-30 17:05:27  来源:网络整理

昨天群里的一个朋友问:如何将进程绑定到某个CPU并运行它。

首先,我们先了解一下将进程绑定到 CPU 的好处。

进程绑定CPU的好处:在多核CPU结构中,每个核都有自己的L1、L2缓存,L3缓存是共享的。如果一个进程在内核之间来回切换,每个内核的缓存命中率都会受到影响。反之,如果进程无论怎么调度都可以一直在一个核上执行,那么L1、L2缓存的数据命中率可以显着提高。

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

设置进程的CPU亲和性

介绍完将进程绑定到CPU的好处之后,现在来介绍一下Linux系统下如何将进程绑定到CPU(即设置进程的CPU亲和性)。

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

intsched_setaffinity(pid_tpid, size_tcpusetsize, constcpu_set_t*mask);

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

参数掩码的类型是cpu_set_t,cpu_set_t是一个位图,该位图的每个位代表一个CPU,如下图所示:

进程 绑定cpu 不起作用

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

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

#define_GNU_SOURCE

# 包含

# 包含

# 包含

#include

# 包含

# 包含

intmain( intargc, char**argv)

{

cpu_set_tcpuset;

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

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

//设置进程的CPU亲和性

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

printf("设置 CPU 关联失败,错误:%s\n", strerror(errno));

return-1;

}

return0;

}

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 函数的实现:

静态

migrate_task(struct task_struct *p, intdest_cpu, struct migration_req *req)

{

structrq * rq = task_rq(p);

// 情况 1:

// 如果进程不在任何运行队列中

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

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

set_task_cpu(p, dest_cpu);

return0;

}

// 情况二:

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

//然后进程需要从之前的CPU可运行队列迁移到新的CPU可运行队列

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

// 构建流程迁移请求

init_completion(&req->done);

req->task = p;

req->dest_cpu = dest_cpu;

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

返回1;

}

首先让我们介绍一下migration_task函数的每个参数的含义:

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

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

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

staticint

__migrate_task(struct task_struct *p, intsrc_cpu, intdest_cpu)

{

structrq* rq_dest, * rq_src;

intret = 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); // 将进程放入目标可运行队列

...

}

...

返回;

}

__migrate_task 函数主要完成以下两个任务:

工作流程如下图(将进程从CPU0的可运行队列迁移到CPU3的可运行队列):

进程 绑定cpu 不起作用

如上图所示,该进程最初位于CPU0的可运行队列中,但是由于该进程已重新绑定到CPU3,因此需要将该进程从CPU0的可运行队列迁移到CPU3的可运行队列。 .

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

当CPU要运行一个进程时,首先从它所属的可运行队列中选择一个进程,并调度这个进程在CPU中运行。

总结

从上面的分析可以看出,将进程绑定到某个CPU,只是将进程放入CPU的可运行队列中。

因为每个CPU都有一个可运行队列,所以可能存在CPU间可运行队列负载不均衡的问题。比如CPU0的可运行队列中的进程比CPU1的可运行队列中的进程多很多,导致CPU0的负载很高,而CPU1的负载很低。


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-379227-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      • 崔郾
        崔郾

        就应该采取利比亚那样

      • 明太祖朱元璋
        明太祖朱元璋

        我本人也喝过很多黑芝麻糊

      热点图片
      拼命载入中...