Linux per-cpu机制

本文介绍了Linux操作系统中per_cpu机制的基本概念与实现原理。该机制允许每个CPU拥有自己的私有数据段,以提高数据访问效率及安全性。文章详细解释了相关宏定义,并通过实例展示了如何定义和初始化per_cpu变量。

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

Linux操作系统,特别是针对SMP或者NUMA架构的多CPU系统的时候,描述每个CPU的私有数据的时候,Linux操作系统提供了per_cpu机制。 

1.1     定义

per_cpu机制就是让每个CPU都有自己的私有数据段,便于保护与访问。

相关宏定义在include/linux/percpu-defs.h文件中:

/*     

 * Normal declaration and definition macros.

 */

#define DECLARE_PER_CPU_SECTION(type, name, sec)                        \

        extern __PCPU_ATTRS(sec) __typeof__(type) name

 

#define DEFINE_PER_CPU_SECTION(type, name, sec)                         \

        __PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES                        \

        __typeof__(type) name

#endif

 

/*

 * Variant on the per-CPU variable declaration/definition theme used for

 * ordinary per-CPU variables.

 */

#define DECLARE_PER_CPU(type, name)                                     \

        DECLARE_PER_CPU_SECTION(type, name, "")

 

#define DEFINE_PER_CPU(type, name)                                      \

        DEFINE_PER_CPU_SECTION(type, name, "")

include/linux/percpu-defs.h文件

#define __PCPU_ATTRS(sec)                                               \

        __percpu __attribute__((section(PER_CPU_BASE_SECTION sec)))     \

        PER_CPU_ATTRIBUTES

这个__PCPU_ATTRS是每个CPU变量声明和定义的最基本实现。总之就是创建CPU的一个私有变量。其中#define PER_CPU_BASE_SECTION ".data..percpu"定义其存放的数据段,定义在arch/ia64/include/asm/percpu.h文件中

1.2     示例

例如定义描述每个IA64CPU信息的数据结构变量ia64_cpu_info, 在文件

arch/ia64/include/asm/processor.hDEFINE_PER_CPU,定义这种私有数据,放在特定的数据段中。

DECLARE_PER_CPU(struct cpuinfo_ia64, ia64_cpu_info);

在文件arch/ia64/include/asm/processor.h中,定义了结构体cpuinfo_ia64。其中定义了CPU类型,硬件BUG标志, CPU状态等。

struct cpuinfo_ia64 {

        unsigned int softirq_pending;  

        unsigned long itm_delta;        /* # of clock cycles between clock ticks */

        unsigned long itm_next;         /* interval timer mask value to use for next clock tick */

        unsigned long nsec_per_cyc;     /* (1000000000<<IA64_NSEC_PER_CYC_SHIFT)/itc_freq */

        unsigned long unimpl_va_mask;   /* mask of unimplemented virtual address bits (from PAL) */

        unsigned long unimpl_pa_mask;   /* mask of unimplemented physical address bits (from PAL) */

        unsigned long itc_freq;         /* frequency of ITC counter */

        unsigned long proc_freq;        /* frequency of processor */

        unsigned long cyc_per_usec;     /* itc_freq/1000000 */

        unsigned long ptce_base;

        unsigned int ptce_count[2];

        unsigned int ptce_stride[2];

        struct task_struct *ksoftirqd;  /* kernel softirq daemon for this CPU */

 

#ifdef CONFIG_SMP

        unsigned long loops_per_jiffy;

        int cpu;

        unsigned int socket_id; /* physical processor socket id */

        unsigned short core_id; /* core id */

        unsigned short thread_id; /* thread id */

        unsigned short num_log; /* Total number of logical processors on

                                 * this socket that were successfully booted */

        unsigned char cores_per_socket; /* Cores per processor socket */

        unsigned char threads_per_core; /* Threads per core */

#endif 

       

        /* CPUID-derived information: */

        unsigned long ppn;

        unsigned long features;

        unsigned char number;

        unsigned char revision;

        unsigned char model;

        unsigned char family;

        unsigned char archrev;

        char vendor[16];

        char *model_name;

 

#ifdef CONFIG_NUMA

        struct ia64_node_data *node_data;

#endif

};

1.3     初始化

  在start_kernel函数中调用执行函数setup_per_cpu_areas( ),其会将.data.percpu中的数据拷贝到每个CPU的数据段中,每个CPU一份。 其中CPU n 对应的专有数据区的首地址为__per_cpu_offset[n]。 

1.4     读取变量

在不同的内核版本中,略有差异,这里取的是最新的2018/3/19日的主线版本中代码。include/linux/percpu-defs.h

#define get_cpu_var(var)                                                \

(*({                                                                    \

        preempt_disable();                                              \

        this_cpu_ptr(&var);                                             \

}))

 

<think>嗯,用户想了解per-cpu_rwsem的实现细节和在内核编程中的使用。首先,我需要回忆一下per-cpu_rwsem的相关知识。per-cpu_rwsem是Linux内核中的一种读写信号量,它通过每CPU计数器来优化传统读写信号量的扩展性问题。传统读写信号量在高竞争场景下性能可能不佳,而per-cpu_rwsem通过减少锁争用来提升性能。接下来,我需要考虑它的实现细节。比如,每CPU计数器是如何工作的,写者如何获取锁,读者如何进入和退出。可能涉及到的结构体有structrw_semaphore,以及相关的函数如percpu_down_read、percpu_up_read等。写者可能需要使用percpu_down_write来获取独占访问权,而读者则使用percpu_down_read来进入临界区。然后,用户可能关心的使用场景,比如RCU机制、内存管理子系统等。需要举一些具体的例子,比如在mm/rmap.c中的使用情况。此外,还需要提到优化方法,比如避免长时间持有写锁,以减少对读者性能的影响。还要注意用户提到的引用部分,虽然之前的引用是关于mutex和上下文切换的开销,但per-cpu_rwsem的设计正是为了减少这种开销,所以可以适当关联。不过用户指出引用仅供参考,所以重点还是放在per-cpu_rwsem本身。可能生成的相关问题包括per-cpu_rwsem传统读写信号量的对比,如何在内核模块中使用它,性能分析,以及常见问题排查。这些问题能帮助用户更全面地理解该机制。</think>### per-cpu_rwsem 实现细节 #### 核心设计 per-cpu_rwsem 是 Linux 内核中针对读写信号量(rw_semaphore)的扩展性优化实现,主要特点: 1. **每CPU计数器**:通过维护每个CPU的本地计数器,减少全局锁争用 2. **写者优先**:使用全局原子计数器处理写者请求,保证写者优先 3. **快速路径**:读者通过`this_cpu_inc()`原子操作快速进入临界区[^1] #### 关键数据结构 ```c // 内核实现片段(简化版) struct rw_semaphore { atomic_t count; struct percpu_counter readers; struct task_struct *writer; }; ``` #### 工作流程 1. **读者进入**: ```c void percpu_down_read(struct rw_semaphore *sem) { preempt_disable(); // 禁用抢占 this_cpu_inc(sem->readers); // 本地计数器递增 smp_mb(); // 内存屏障 } ``` 2. **写者获取**: ```c void percpu_down_write(struct rw_semaphore *sem) { atomic_inc(&sem->count); // 全局计数器递增 synchronize_rcu(); // 等待现有读者退出 } ``` #### 性能优化 - **批量更新**:通过`percpu_counter_sum()`合并多个CPU的计数器更新 - **RCU同步**:利用RCU机制实现无锁读者检测 - **自适应切换**:当检测到写者等待时,自动切换到传统读写信号量模式 ### 典型使用场景 1. **RCU机制**:用于保护RCU宽限期(grace period)[^1] 2. **内存管理**:在mm/rmap.c中保护反向映射数据结构 3. **文件系统**:XFS等文件系统的元数据保护 ### 开发注意事项 1. **读者上下文**:必须保证读者在临界区内不睡眠 2. **写者延迟**:写者需要等待所有现有读者退出 3. **调试支持**:可通过`CONFIG_DEBUG_RWSEMS`开启调试检查
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值