linux smp_setup_processor_id

博客介绍了SMP(对称多处理模型)、NUMA(非一致性存储访问结构)和MPP(海量并行处理结构)。SMP中多个CPU平等共享资源,但扩展性差;NUMA将CPU分模块,各模块有独立内存等,CPU地位不一致;MPP由多个SMP服务器互联。还提及支持不同结构的CPU及相关函数逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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");
}

 

<think>我们正在讨论Linux内核中的`void__init`函数用法。根据用户的问题,用户希望了解`__init`宏在内核初始化函数中的作用和用法。根据引用[2]和[3],我们可以看到在内核启动过程中(start_kernel函数里)会调用很多标记为`__init`的函数,比如`debug_objects_early_init`。这些函数在初始化完成后会被释放,以节省内存。因此,我们可以这样解释: `__init`宏用于标记内核初始化期间使用的函数。这些函数被放在特定的内存段中(.init.text),当初始化完成后,整个.init段的内存会被释放。此外,引用[1]中展示了一个宏定义`console_initcall`,它也是用于初始化调用的,但这里我们主要关注`__init`。下面我们详细说明:</think>### Linux内核中`void __init`函数用法解析 `void __init`是Linux内核中用于**初始化函数**的特殊标记,其核心作用是在内核启动阶段执行一次性初始化任务,并在初始化完成后自动释放相关内存空间。 #### 关键特性 1. **内存管理机制** - 通过`__init`宏定义(位于`include/linux/init.h`)实现: ```c #define __init __section(".init.text") __cold ``` - 编译时将被标记的函数/数据放入`.init.text`和`.init.data`段 - 内核启动完成后(`start_kernel()`→`rest_init()`阶段),通过`free_initmem()`释放这些内存[^2][^3] 2. **典型使用场景** ```c void __init debug_objects_early_init(void) { // 初始化调试对象哈希表 for (i = 0; i < ODEBUG_HASH_SIZE; i++) raw_spin_lock_init(&obj_hash[i].lock); // 填充静态对象池 for (i = 0; i < ODEBUG_POOL_SIZE; i++) hlist_add_head(&obj_static_pool[i].node, &obj_pool); } // [^2] ``` - 硬件抽象层初始化(如`smp_setup_processor_id()`[^2]) - 内核子系统预配置(如调度器`rcu_scheduler_starting()`[^3]) - 关键数据结构预分配 3. **与初始化调用的关联** ```c #define console_initcall(fn) \ static initcall_t __initcall_##fn \ __used __section(.con_initcall.init) = fn // [^1] ``` - `__init`标记函数体,而`initcall`宏定义初始化调用顺序 - 两者协同实现**阶段化初始化**(从`early_initcall`到`late_initcall`) #### 设计优势 1. **内存效率** 释放内核初始化后不再需要的代码/数据(可回收数百KB内存) 2. **启动优化** 确保初始化函数按正确顺序执行(通过`.initcall`分级) 3. **错误隔离** 初始化失败时提供明确调试路径(如`initcall_debug`参数) #### 使用规范 1. 仅用于**一次性**初始化逻辑 2. 禁止在模块中使用(仅限核心内核) 3. 函数命名需明确体现初始化目标(如`xxx_early_init`) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值