Linux多核运行机制(SMP)

这篇具有很好参考价值的文章主要介绍了Linux多核运行机制(SMP)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、Linux内核兼容多处理器要求

二、UP

三、SMP

3.1、概念

3.2、SMP的优势与缺陷

3.3、SMP启动

3.4、SMP拓扑关系构建

3.5、SMP调度机制分析

3.6、SMP调度时机

3.6.1、scheduler_tick

 四、常用命令

4.1、查看逻辑CPU个数

4.2、查看每个CPU核心数


一、Linux内核兼容多处理器要求

        有多个 CPU 处理器 的 系统中 , Linux 内核需要处理的问题 :

                1、公平共享 : CPU 的负载 , 需要公平地共享 , 不能出现某个CPU空闲 , 造成资源浪费。

                2、可设置进程 与 CPU 亲和性 : 可以为 某些类型的 进程 与 指定的 处理器设置亲和性 , 可以针对性地匹配 进程与处理器 。

                3、进程迁移 : Linux 内核可以将进程在不同的CPU处理器之间进行迁移 。

        Linux 内核 的SMP对称多处理器结构调度,核心就是将进程迁移到合适的 处理器上 , 并且可以保持各个处理器的负载均衡。

二、UP

        UP(Uni-Processor):系统只有一个处理器单元,即单核CPU系统。

三、SMP

3.1、概念

        对称多处理器结构 , 英文名称为 " Symmetrical Multi-Processing " , 简称 SMP 。

        SMP 又称为 UMA , 全称 " Uniform Memory Access " , 中文名称 " 统一内存访问架构 " 。

        在 " 对称多处理器结构 " 的 系统中 , 所有的处理器单元的地位都是平等的 , 一般指的是服务器设备上 , 运行的 多个 CPU , 没有 主次/从属 关系,都是平等的。

        这些处理器 共享 所有的设备资源 , 所有的资源对处理器单元具有相同的可访问性 , 如 : 磁盘 , 内存 , 总线等 ,多个CPU处理器共享相同的物理内存 , 每个 CPU 访问相同的物理地址 , 所消耗的时间是相同的 ;

        要注意,这里提到的“处理器单元”是指“logic CPU”,而不是“physical CPU”。举个例子,如果一个“physical CPU”包含2个core,并且一个core包含2个hardware thread。则一个“处理器单元”就是一个hardware thread。

3.2、SMP的优势与缺陷

        优点 :避免了 结构障碍 , 其最大的特点是 所有的资源共享。

        缺点:SMP 架构的系统 , 扩展能力有限 , 有瓶颈限制。

        如 : 内存瓶颈限制 , 每个 CPU 处理器必须通过 相同的总线 访问 相同的内存资源 , 如果 CPU 数量不断增加 , 使用同一条总线 , 就会导致 内存访问冲突 ; 这样就降低了 CPU 的性能 ;

        通过实践证明 , SMP 架构的系统 , 使用2 ~ 4个 CPU , 可以达到利用率最高 , 如果 CPU 再多 , 其利用率就会降低 , 浪费处理器的性能 。

3.3、SMP启动

        SMP结构中的CPU都是平等的,没有主次之分,这是基于系统中有多个进程的前提下说的。

        在同一时间,一个进程只能由一个CPU执行。

        系统启动对于SMP结构来说是一个特例,因为在这个阶段系统里只有一个CPU,也就是说在刚刚上电或者总清时只有一个CPU来执行系统引导和初始化。

        这个CPU被称为“引导处理器”,即BP,其余的处理器处于暂停状态,称为“应用处理器”,即AP。

max_newidle_lb_cost,Linux,# Linux 驱动,# Linux Kernel,linux,驱动开发

max_newidle_lb_cost,Linux,# Linux 驱动,# Linux Kernel,linux,驱动开发

         "引导处理器"完成整个系统的引导和初始化,并创建起多个进程,从而可以由多个处理器同时参与处理时,才启动所有的"应用处理器",让他们完成自身的初始化以后,投入运行。

                1、BP先完成自身初始化,然后从start_kernel()调用smp_init()进行SMP结构初始化。

                2、smp_init()的主体是smp_boot_cpus(),依次调用do_boot_cpu()启动各个AP。

                3、AP通过执行trampoline.S的一段跳板程序进入startup_32()完成一些基本初始化。

                4、AP进入start_secondary()做进一步初始化工作,进入自旋(全局变量smp_commenced是否变为1),等待一个统一的“起跑”命令。

                5、BP完成所有AP启动后,调用smp_commence()发出该起跑命令。

                6、每个CPU进入cpu_idle(),等待调度。

3.4、SMP拓扑关系构建

        系统启动时开始构建CPU拓扑关系。

max_newidle_lb_cost,Linux,# Linux 驱动,# Linux Kernel,linux,驱动开发

        ARM中,4核处理器示意图如下所示:

max_newidle_lb_cost,Linux,# Linux 驱动,# Linux Kernel,linux,驱动开发

         上述4核处理器最后生成的调度域与调度组的拓扑关系图如下图如示:

max_newidle_lb_cost,Linux,# Linux 驱动,# Linux Kernel,linux,驱动开发

3.5、SMP调度机制分析

        1、首先,load_balance()调用find_busiest_queue()来选出最忙的运行队列,在这个队列中具有最多的进程数。这个最忙的运行队列应该至少比当前的队列多出25%的进程数量。如果不存在具备这样条件的队列,find_busiest_queue()函数NULL,同时load_balance()也返回。如果存在,那么将返回这个最忙的运行时队列。

        2、然后,load_balance()函数从这个最忙的运行时队列中选出将要进行负载平衡的优先级数组(priority array)。选取优先级数组原则是,首先考虑过期数组(expired array),因为这个数组中的进程相对来说已经很长时间没有运行了,所以它们极有可能不在处理器缓冲中。如果过期数组(expired priority array)为空,那就只能选择活跃数组(active array)。

        3、下一步,load_balance()找出具有最高优先级(最小的数字)链表,因为把高优先级的进程分发出去比分发低优先级的更重要。

        4、为了能够找出一个没有运行,可以迁移并且没有被缓冲的进程,函数将分析每一个该队列中的进程。如果有一个进程符合标准,pull_task()函数将把这个进程从最忙的运行时队列迁移到目前正在运行的队列。

        5、只要这个运行时队列还处于不平衡的状态,函数将重复执行3和4,直到将多余进程从最忙的队列中迁移至目前正在运行的队列。最后,系统又处于平衡状态,当前运行队列解锁。load_balance()返回。

3.6、SMP调度时机

        1、scheduler_tick

        2、try_to_wake_up(优先选择在唤醒的CPU上运行)。

        3、exec系统调用启动一个新进程时。

3.6.1、scheduler_tick

        系统的软中断触发会周期性的调度scheduler_tick函数,每个cpu都有一个时钟中断,都会被周期性的调度到scheduler_tick函数。

/*
 * This function gets called by the timer code, with HZ frequency.
 * We call it with interrupts disabled.
 */
void scheduler_tick(void)
{
    int cpu = smp_processor_id();
    struct rq *rq = cpu_rq(cpu);
    struct task_struct *curr = rq->curr;
    struct rq_flags rf;
 
    sched_clock_tick();
 
    rq_lock(rq, &rf);
 
    walt_set_window_start(rq, &rf);
    walt_update_task_ravg(rq->curr, rq, TASK_UPDATE,
                        walt_ktime_clock(), 0);
    update_rq_clock(rq);
    curr->sched_class->task_tick(rq, curr, 0);
    cpu_load_update_active(rq);
    calc_global_load_tick(rq);
    psi_task_tick(rq);
 
    rq_unlock(rq, &rf);
 
    perf_event_task_tick();
 
#ifdef CONFIG_SMP
    rq->idle_balance = idle_cpu(cpu);
    trigger_load_balance(rq);
#endif
    rq_last_tick_reset(rq);
 
    if (curr->sched_class == &fair_sched_class)
        check_for_migration(rq, curr);
}

        scheduler_tick主要完成的任务如下:

                1、更新WALT统计的。

                2、更新系统时钟。

                3、更新PELT方式统计的cpu级别的负载统计。

                4、更新系统级的负载统计。

                5、触发负载均衡trigger_load_balance。

/*
 * Trigger the SCHED_SOFTIRQ if it is time to do periodic load balancing.
 */
void trigger_load_balance(struct rq *rq)
{
        /* Don't need to rebalance while attached to NULL domain */
        if (unlikely(on_null_domain(rq)))
                return;
 
/*
#define time_after_eq(a,b)    \ 
    (typecheck(unsigned long, a) && \ 
   typecheck(unsigned long, b) && \ 
   ((long)(a) - (long)(b) >= 0)) 
在宏中,参数 a 是 jiffies 在某个时刻的快照,如果 a 所代表的时间比 b 靠后或者相等,那么返回真
*/
        if (time_after_eq(jiffies, rq->next_balance))
                raise_softirq(SCHED_SOFTIRQ);
#ifdef CONFIG_NO_HZ_COMMON
        if (nohz_kick_needed(rq, false))
                nohz_balancer_kick(false);
#endif
}

        绑定的软中断的处理函数run_rebalance_domains。CONFIG_NO_HZ_COMMON这个宏是已经定义的。

/*
 * run_rebalance_domains is triggered when needed from the scheduler tick.
 * Also triggered for nohz idle balancing (with nohz_balancing_kick set).
 */
static __latent_entropy void run_rebalance_domains(struct softirq_action *h)
{
        struct rq *this_rq = this_rq();
        enum cpu_idle_type idle = this_rq->idle_balance ?
                                                CPU_IDLE : CPU_NOT_IDLE;
 
        /*
         * If this cpu has a pending nohz_balance_kick, then do the
         * balancing on behalf of the other idle cpus whose ticks are
         * stopped. Do nohz_idle_balance *before* rebalance_domains to
         * give the idle cpus a chance to load balance. Else we may
         * load balance only within the local sched_domain hierarchy
         * and abort nohz_idle_balance altogether if we pull some load.
         */
        nohz_idle_balance(this_rq, idle);
        update_blocked_averages(this_rq->cpu);
#ifdef CONFIG_NO_HZ_COMMON
        if (!test_bit(NOHZ_STATS_KICK, nohz_flags(this_rq->cpu)))
                rebalance_domains(this_rq, idle);
        clear_bit(NOHZ_STATS_KICK, nohz_flags(this_rq->cpu));
#else
        rebalance_domains(this_rq, idle);
#endif
}
/*
 * It checks each scheduling domain to see if it is due to be balanced,
 * and initiates a balancing operation if so.
 *
 * Balancing parameters are set up in init_sched_domains.
 */
 /*
根据domain级别,从下往上扫描每一级sched_domain。
如果这个domain balance之间间隔时间到了,就进行load_balance操作,
不同级别的domain时间间隔不同,而且级别越高,间隔越长,因为迁移task代价越来越大。
*/
static void rebalance_domains(struct rq *rq, enum cpu_idle_type idle)
{
        int continue_balancing = 1;
        int cpu = rq->cpu;
        unsigned long interval;
        struct sched_domain *sd;
        /* Earliest time when we have to do rebalance again */
        /* 默认本cpu rq下一次的balance时间为60 tick以后 */
        unsigned long next_balance = jiffies + 60*HZ;
        int update_next_balance = 0;
        int need_serialize, need_decay = 0;
        u64 max_cost = 0;
 
        rcu_read_lock();
        /* (2) 对本cpu每个层次的schedule_domain进行扫描 */
        for_each_domain(cpu, sd) {//遍历该cpu的所有调度域,从最低一级到最高一级。
                /*
                 * Decay the newidle max times here because this is a regular
                 * visit to all the domains. Decay ~1% per second.
                 */
                 // max_newidle_lb_cost 是做load balance所花时间。如上面注释所说,max_newidle_lb_cost每个1s衰减1%
                 // next_decay_max_lb_cost 是下一次进行衰减的时间
                 // 老化公式: new = old * (253/256)
                if (time_after(jiffies, sd->next_decay_max_lb_cost)) {
                        sd->max_newidle_lb_cost =
                                (sd->max_newidle_lb_cost * 253) / 256;
                        sd->next_decay_max_lb_cost = jiffies + HZ;
                        need_decay = 1;
                }
                max_cost += sd->max_newidle_lb_cost;
 
                if (energy_aware() && !sd_overutilized(sd) && !sd->parent)
                        continue;
 
                if (!(sd->flags & SD_LOAD_BALANCE)) {//该调度域被指定不进行负载均衡
                        if (time_after_eq(jiffies,
                                          sd->groups->sgc->next_update))
                                update_group_capacity(sd, cpu);
                        continue;
                }
 
                /*
                 * Stop the load balance at this level. There is another
                 * CPU in our sched group which is doing load balancing more
                 * actively.
                 */
                 /* (4) 如果continue_balancing = 0,指示停止当前层级的load balance
            因为shed_group中其他的cpu正在这个层次做load_balance*/
                if (!continue_balancing) {
                        if (need_decay)
                                continue;
                        break;
                }
                /* (5) 计算当前层次schedule_domain的balance间隔时间 */
                interval = get_sd_balance_interval(sd, idle != CPU_IDLE);
                 /* (6) 如果需要串行化(SD_SERIALIZE),做balance之前需要持锁 */
                need_serialize = sd->flags & SD_SERIALIZE;
                if (need_serialize) {
                        if (!spin_trylock(&balancing))
                                goto out;
                }
                /* (7) 如果本sd的balance间隔时间已到,进行实际的load_balance() */
                //load_balance检查该cpu在这一层的调度域中是否存在负载不平衡的情况,如果存在该cpu会分担负载最重的那个cpu的一些任务
                if (time_after_eq(jiffies, sd->last_balance + interval)) {
                        if (load_balance(cpu, rq, sd, idle, &continue_balancing)) {
                                /*
                                 * The LBF_DST_PINNED logic could have changed
                                 * env->dst_cpu, so we can't know our idle
                                 * state even if we migrated tasks. Update it.
                                 */
                                idle = idle_cpu(cpu) ? CPU_IDLE : CPU_NOT_IDLE;
                        }
                        sd->last_balance = jiffies;
                        interval = get_sd_balance_interval(sd, idle != CPU_IDLE);
                }
                if (need_serialize)
                        spin_unlock(&balancing);
out:
 
                //到达执行load balance的时间
                if (time_after(next_balance, sd->last_balance + interval)) {
                        next_balance = sd->last_balance + interval;
                        update_next_balance = 1;
                }
        }
        if (need_decay) {
                /*
                 * Ensure the rq-wide value also decays but keep it at a
                 * reasonable floor to avoid funnies with rq->avg_idle.
                 */
                rq->max_idle_balance_cost =
                        max((u64)sysctl_sched_migration_cost, max_cost);
        }
        rcu_read_unlock();
 
        /*
         * next_balance will be updated only when there is a need.
         * When the cpu is attached to null domain for ex, it will not be
         * updated.
         */
         /* (8.1) 更新rq的balance时间 */
        if (likely(update_next_balance)) {
                rq->next_balance = next_balance;
 
#ifdef CONFIG_NO_HZ_COMMON
                /*
                 * If this CPU has been elected to perform the nohz idle
                 * balance. Other idle CPUs have already rebalanced with
                 * nohz_idle_balance() and nohz.next_balance has been
                 * updated accordingly. This CPU is now running the idle load
                 * balance for itself and we need to update the
                 * nohz.next_balance accordingly.
                 */
                if ((idle == CPU_IDLE) && time_after(nohz.next_balance, rq->next_balance))
                        nohz.next_balance = rq->next_balance;
#endif
        }
}

 四、常用命令

4.1、查看逻辑CPU个数

        方法一:cat /proc/cpuinfo

 processor : 11
 vendor_id : GenuineIntel
 cpu family : 6
 model : 62
 model name : Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz
 stepping : 4
 microcode : 1064
 cpu MHz : 2100.170
 cache size : 15360 KB
 physical id : 0
 siblings : 12
 core id : 5
 cpu cores : 6
 apicid : 11
 initial apicid : 11
 fpu : yes
 fpu_exception : yes
 cpuid level : 13
 wp : yes
 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat xsaveopt pln pts dts tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms
 bogomips : 4200.34
 clflush size : 64
 cache_alignment : 64
 address sizes : 46 bits physical, 48 bits virtual
 power management:

        可以看出有12个逻辑CPU。因为processor从0开始,到11结束,说明有12个。

        方法二:cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c

[root@localhost ~]# cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
     12  Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz

4.2、查看每个CPU核心数

        cat /proc/cpuinfo |grep “cores”

[root@localhost ~]# cat /proc/cpuinfo |grep "cores"
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6

        可以看到,12个逻辑CPU中,每个CPU都是6核。其实在查看CPU基本信息里,有个cpu cores:6,直接就显示是6个。 文章来源地址https://www.toymoban.com/news/detail-812331.html

到了这里,关于Linux多核运行机制(SMP)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android HAL机制的深入理解及在Linux上移植和运行的一个好玩的HAL小例子

    PS:要转载请注明出处,本人版权所有。 PS: 这个只是基于《我自己》的理解, 如果和你的原则及想法相冲突,请谅解,勿喷。 环境说明   Ubuntu 18.04.x   近一年来,虽然还是做的是AIOT相关的事情,但是某些事情却发生了一些变化。随着个人的阅历提升,现在的AI在边缘端

    2023年04月08日
    浏览(46)
  • ‘“node --max-old-space-size=10240“‘不是内部或外部命令,也不是可运行的程序

    在运行vue项目,执行“npm run dev”的时候,一直报错:\\\' \\\"node --max-old-space-size=10240\\\"\\\'不是内部或外部命令,也不是可运行的程序。 这里不是内存设置太大,而是因为win10系统命令行中不能正确识别双引号\\\"\\\",所以要把这个插件包中涉及到的脚本中双引号都去掉,即 修改node_modul

    2024年02月12日
    浏览(39)
  • StratoVirt 的 vCPU 拓扑(SMP)

    CPU 拓扑用来表示 CPU 在硬件层面的组合方式,本文主要讲解 CPU 拓扑中的 SMP(Symmetric Multi-Processor,对称多处理器系统)架构,CPU 拓扑还包括其他信息,比如:cache 等,这些部分会在后面进行补充。CPU 拓扑除了描述 CPU 的组成关系外,还为内核的调度器提供服务,从而提供更

    2024年02月07日
    浏览(34)
  • BLE协议--SMP(安全管理协议)

    目录 前言 1. Security Manager介绍 2. Pairing(配对) 2.1 配对第一阶段 2.1.1 配对方法 2.1.2 Authentication(鉴权方式) 2.1.3 IO Capabilities  3.1.4 Authentication方法的选择 2.2 LE legacy pairing 2.2.1 配对请求的报文格式 2.2.2 配对第二阶段 2.2.3 配对第三阶段 3. 绑定 总结 SMP即Security Manager Protocol。

    2024年02月06日
    浏览(45)
  • 关于Windows 11 docker desktop 运行doris 容器时vm.max_map_count=2000000的设置问题

    需要一个简单的测试环境,于是准备用docker启动一个1fe 1be的简单玩一下 如果be容器启动后再去修改 /etc/sysctl.conf sysctl -w vm.max_map_count=2000000 这个参数是没用的,be仍然会启动失败 这时可以打开cmd wsl --list C:Userspcwsl --list 适用于 Linux 的 Windows 子系统分发: Ubuntu (默认) docker-des

    2024年02月11日
    浏览(50)
  • 异核通信框架(1)——SMP和AMP架构

            我是菜鸡,很久没有发表文章了。老样子,今天推荐一本书《局外人》。别像主人公似的认为任何事情都没有意义。 目录 1. SMP和 AMP架构 1.1 同构和异构         1.1.1  同构         1.1.2  异构 1.2 SMP和AMP 1.2.1  对称多处理结构(SMP) 1.2.2  非对称多处理结构(AMP)

    2024年02月08日
    浏览(84)
  • ARM学习(23)AMP和SMP的认识与理解

    笔者来聊聊AMP和SMP架构理解(多核下系统)。 笔者经常听到ARM架构时,谈到SMP的架构或者AMP的架构,今天特意来了解一下,主要是针对多core处理,对于常见的MCU应用场景,可以比较少,往往是需要较强的性能或者应付复杂的场景,会碰到多核的场景。 SMP:Symmetric multiproces

    2024年02月12日
    浏览(106)
  • 真正理解微软Windows程序运行机制——窗口机制(第三部分)

    我是荔园微风,作为一名在IT界整整25年的老兵,今天说说Windows程序的运行机制。经常被问到MFC到底是一个什么技术,为了解释这个我之前还写过帖子,但是很多人还是不理解。其实这没什么,我在学生时代也被这个问题困绕过。而且那个时间学习资料没有那么丰富,网上也

    2024年02月16日
    浏览(35)
  • 真正理解微软Windows程序运行机制——窗口机制(第二部分)

    我是荔园微风,作为一名在IT界整整25年的老兵,今天说说Windows程序的运行机制。经常被问到MFC到底是一个什么技术,为了解释这个我之前还写过帖子,但是很多人还是不理解。其实这没什么,我在学生时代也被这个问题困绕过。而且那个时间学习资料没有那么丰富,网上也

    2023年04月11日
    浏览(42)
  • 微软MFC技术运行机制

    我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下微软MFC技术运行机制。 很多初学者误以为VC++开发必须使用MFC,其实不一定的。MFC的使用只能是提高程序在某些情况下的开发效率,而不能替代整个Win32程序设计。我认为我们有必要再来好好讲讲MFC的本质、MFC中的

    2024年02月08日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包