Linux smp_setup_processor_id
阅读数:263 评论数:0
跳转到新版页面分类
Linux
正文
一、概述
在Linux内核中,smp_setup_processor_id
函数是在系统引导期间,特别是在多处理器系统中用于设置当前处理器ID的函数。这个函数在早期的系统初始化代码中调用,其目的是确定当前运行的CPU,并设置全局变量 smp_processor_id
,该变量在内核的其他部分用来识别当前的处理器。
简单来说,smp_setup_processor_id
的作用包括:
- 确定当前CPU的ID。
- 初始化与该CPU相关的数据结构。
- 在多处理器系统中,可能还会检测其他处理器的存在和状态。
这个函数对于系统的多处理器支持(SMP,Symmetric MultiProcessing)是非常关键的。在启动过程中,内核需要知道哪个CPU正在执行代码,以便正确地初始化系统和管理不同的处理器。
在现代Linux内核中,这个函数的实现和具体作用可能随着架构(如x86、ARM等)和内核版本的不同而有所变化。它通常是架构特定代码的一部分,因为不同的处理器架构在启动和初始化处理器时有不同的要求和机制。
在内核启动的早期阶段,smp_setup_processor_id
通常是在架构特定的启动代码中调用的,这发生在执行架构无关初始化代码之前。这样做是为了确保当内核开始执行更高级别的初始化代码时,它已经知道了当前处理器的身份。
由于这部分代码是针对系统引导和低级处理器管理的,一般开发者在编写普通的内核模块或驱动时不会直接与之交互。这些细节通常由内核的底层架构代码处理,而不需要普通开发者关心
二、源码相关
smp模型指的是对称多处理模型(Symmetric Multi-Processor),与它对应的是NUMA非一致性存储访问结果(Non-Uniform Memory Access)和MPP海量并行处理结构(Massive Parallel Processing)。它们的区别分别在于,SMP指的是多个CPU之间是平等关系,共享全部总线,内存和IO等。但是这个结构扩展性不好,往往CPU数量多了之后,很容易遇到抢占资源的问题。NUMA结构则是把CPU分模块,每个模块具有独立的内存,IO插槽等。各个模块之间通过互联模块进行数据交互。但是这样,就表示了有的内存数据在这个CPU模块中,那么处理这个数据当然最好是选择当前的CPU模块,这样每个CPU实际上地位就不一致了。所以叫做非一致性的存储访问结构。而MPP呢,则是由多个SMP服务器通过互联网连接起来,支持SMP模型的CPU有AMD/AMD64,而支持NUMA的的X86,而smp_setup_process_id在普通情况下是空实现,在不同的体系,比如arc/arm/kernel/setup.c就有对应的逻辑。
//.../arch/arm/kernel/setup.c
void __init smp_setup_processor_id(void)
{
int i;
//判断是否是smp系统,如果是则从arm协处理器读取当前cpuid,否则为0
u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0;
//根据level确定cpu号,即cpu=(mpidr>>0)&0xff
u32 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
//设置cpu的map数组
//#define cpu_logical_map(cpu) __cpu_logical_map[cpu]
cpu_logical_map(0) = cpu;
//nr_cpu_ids表示系统中cpu总数
for (i = 1; i < nr_cpu_ids; ++i)
cpu_logical_map(i) = i == cpu ? 0 : i;
set_my_cpu_offset(0);
pr_info("Booting Linux on physical CPU 0x%x\n", mpidr);
}
//检测是否为SMP架构
static inline bool is_smp(void)
{
#ifndef CONFIG_SMP
return false;
//CONFIG_SMP_ON_UP表示可以支援SMP Kernel运行在UniProcessor(單核心)的处理器上
#elif defined(CONFIG_SMP_ON_UP)
extern unsigned int smp_on_up;
return !!smp_on_up;
#else
return true;
#endif
}
//arch/arm/include/asm/cputype.h
static inline unsigned int __attribute_const__ read_cpuid_mpidr(void)
{
//从arm协处理器CP15的c0中读取当前cpu id
return read_cpuid(CPUID_MPIDR);
}
#define CPUID_MPIDR 5
#define read_cpuid(reg) \
({ \
unsigned int __val; \
asm("mrc p15, 0, %0, c0, c0, " __stringify(reg) \
: "=r" (__val) \
: \
: "cc"); \
__val; \
})
#define MPIDR_AFFINITY_LEVEL(mpidr, level) \
((mpidr >> (MPIDR_LEVEL_BITS * level)) & MPIDR_LEVEL_MASK)
static inline void set_my_cpu_offset(unsigned long off)
{
//Set TPIDRPRW
asm volatile("mcr p15, 0, %0, c13, c0, 4" : : "r" (off) : "memory");
}