标签:
init/main.c
/*
* Activate the first processor.
*/
static void __init boot_cpu_init(void)
{
int cpu = smp_processor_id();
/* Mark the boot cpu "present", "online" etc for SMP and UP case */
set_cpu_online(cpu, true);
set_cpu_active(cpu, true);
set_cpu_present(cpu, true);
set_cpu_possible(cpu, true);
}
active第一个CPU,默认第一个CPU为boot CPU.
include/linux/smp.h
/*
* smp_processor_id(): get the current CPU ID.
*
* if DEBUG_PREEMPT is enabled then we check whether it is
* used in a preemption-safe way. (smp_processor_id() is safe
* if it‘s used in a preemption-off critical section, or in
* a thread that is bound to the current CPU.)
*
* NOTE: raw_smp_processor_id() is for internal use only
* (smp_processor_id() is the preferred variant), but in rare
* instances it might also be used to turn off false positives
* (i.e. smp_processor_id() use that the debugging code reports but
* which use for some reason is legal). Don‘t use this to hack around
* the warning message, as your code might not work under PREEMPT.
*/
#ifdef CONFIG_DEBUG_PREEMPT
extern unsigned int debug_smp_processor_id(void);
# define smp_processor_id() debug_smp_processor_id()
#else
# define smp_processor_id() raw_smp_processor_id()
#endif
得到当前CPU的ID
在我的代码中定义了CONFIG_DEBUG_PREEMPT宏,所以调用
lib/smp_processor.c
vnotrace unsigned int debug_smp_processor_id(void)
{
int this_cpu = raw_smp_processor_id();
if (likely(preempt_count()))
goto out;
if (irqs_disabled())
goto out;
/*
* Kernel threads bound to a single CPU can safely use
* smp_processor_id():
*/
if (cpumask_equal(tsk_cpus_allowed(current), cpumask_of(this_cpu)))
goto out;
/*
* It is valid to assume CPU-locality during early bootup:
*/
if (system_state != SYSTEM_RUNNING)
goto out;
/*
* Avoid recursion:
*/
preempt_disable_notrace();
if (!printk_ratelimit())
goto out_enable;
printk(KERN_ERR "BUG: using smp_processor_id() in preemptible [%08x] "
"code: %s/%d\n",
preempt_count() - 1, current->comm, current->pid);
print_symbol("caller is %s\n", (long)__builtin_return_address(0));
dump_stack();
out_enable:
preempt_enable_no_resched_notrace();
out:
return this_cpu;
}
EXPORT_SYMBOL(debug_smp_processor_id);
我们只看raw_smp_processor_id函数:
acrh/x86/include/asm/smp.h
#ifdef CONFIG_X86_32_SMP
/*
* This function is needed by all SMP systems. It must _always_ be valid
* from the initial startup. We map APIC_BASE very early in page_setup(),
* so this is correct in the x86 case.
*/
#define raw_smp_processor_id() (this_cpu_read(cpu_number))
extern int safe_smp_processor_id(void);
#elif defined(CONFIG_X86_64_SMP)
#define raw_smp_processor_id() (this_cpu_read(cpu_number))
32位情况下调用this_cpu_read(cpu_number)
先看一下cpu_number的定义:
DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number);
EXPORT_PER_CPU_SYMBOL(cpu_number);
PER_CPU变量
定义一个type为int,name为cpu_number的PER_CPU变量
include/linux/percpu-defs.h
#define DEFINE_PER_CPU_READ_MOSTLY(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "..readmostly")
include/linux/percpu-defs.h
/*
* s390 and alpha modules require percpu variables to be defined as
* weak to force the compiler to generate GOT based external
* references for them. This is necessary because percpu sections
* will be located outside of the usually addressable area.
*
* This definition puts the following two extra restrictions when
* defining percpu variables.
*
* 1. The symbol must be globally unique, even the static ones.
* 2. Static percpu variables cannot be defined inside a function.
*
* Archs which need weak percpu definitions should define
* ARCH_NEEDS_WEAK_PER_CPU in asm/percpu.h when necessary.
*
* To ensure that the generic code observes the above two
* restrictions, if CONFIG_DEBUG_FORCE_WEAK_PER_CPU is set weak
* definition is used for all cases.
*/
#if defined(ARCH_NEEDS_WEAK_PER_CPU) || defined(CONFIG_DEBUG_FORCE_WEAK_PER_CPU)
/*
* __pcpu_scope_* dummy variable is used to enforce scope. It
* receives the static modifier when it‘s used in front of
* DEFINE_PER_CPU() and will trigger build failure if
* DECLARE_PER_CPU() is used for the same variable.
*
* __pcpu_unique_* dummy variable is used to enforce symbol uniqueness
* such that hidden weak symbol collision, which will cause unrelated
* variables to share the same address, can be detected during build.
*/
#define DECLARE_PER_CPU_SECTION(type, name, sec) \
extern __PCPU_DUMMY_ATTRS char __pcpu_scope_##name; \
extern __PCPU_ATTRS(sec) __typeof__(type) name
#define DEFINE_PER_CPU_SECTION(type, name, sec) \
__PCPU_DUMMY_ATTRS char __pcpu_scope_##name; \
extern __PCPU_DUMMY_ATTRS char __pcpu_unique_##name; \
__PCPU_DUMMY_ATTRS char __pcpu_unique_##name; \
extern __PCPU_ATTRS(sec) __typeof__(type) name; __PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES __weak __typeof__(type) name
#else
/*
* 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
ARCH_NEEDS_WEAK_PER_CPU和CONFIG_DEBUG_FORCE_WEAK_PER_CPU都没有被定义,所以调用
__PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES __typeof__(type) name
即
__PCPU_ATTRS("..readmostly") PER_CPU_DEF_ATTRIBUTES __typeof__(int) cpu_number
__PCPU_ATTRS定义在include/linux/percpu-defs.h
/*
* Base implementations of per-CPU variable declarations and definitions, where
* the section in which the variable is to be placed is provided by the
* ‘sec‘ argument. This may be used to affect the parameters governing the
* variable‘s storage.
*
* NOTE! The sections for the DECLARE and for the DEFINE must match, lest
* linkage errors occur due the compiler generating the wrong code to access
* that section.
*/
#define __PCPU_ATTRS(sec) \
__percpu __attribute__((section(PER_CPU_BASE_SECTION sec))) PER_CPU_ATTRIBUTES
__percpu在include/linux/compiler.h被定义为空
PER_CPU_ATTRIBUTES在include/asm-generic/percpu.h被定义为空
PER_CPU_DEF_ATTRIBUTES在include/asm-generic/percpu.h被定义为空
PER_CPU_BASE_SECTION定义在include/asm-generic/percpu.h
#ifndef PER_CPU_BASE_SECTION
#ifdef CONFIG_SMP
#define PER_CPU_BASE_SECTION ".data..percpu"
#else
#define PER_CPU_BASE_SECTION ".data"
#endif
#endif
我的代码中PER_CPU_BASE_SECTION被定义为”.data..percpu”
所以最终展开得到
__attribute__((section(".data..percpu" "..readmostly")))
__typeof__(int) cpu_number
“gcc支持一种叫做类型识别的技术,通过typeof(x)关键字,获得x的数据类型。而如果是在一个要被一些c文件包含的头文件中获得变量的数据类型,就需要用_typeof_而不是typeof关键字了,比如说我们这里。最后,这里就是声明一个int类型的cpu_number变量,编译的时候把他指向.data..percpu段的开始位置”
(引号内这段摘抄自http://blog.chinaunix.net/uid-27717694-id-4033413.html)
我们回到raw_smp_processor_id()宏,看一下this_cpu_read(cpu_number)
include/linux/percpu.h
# define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, (pcp))
include/linux/percpu.h
#define __pcpu_size_call_return(stem, variable) \
({ typeof(variable) pscr_ret__; __verify_pcpu_ptr(&(variable)); switch(sizeof(variable)) { case 1: pscr_ret__ = stem##1(variable);break; \
case 2: pscr_ret__ = stem##2(variable);break; \
case 4: pscr_ret__ = stem##4(variable);break; \
case 8: pscr_ret__ = stem##8(variable);break; \
default: __bad_size_call_parameter();break; } pscr_ret__; })
再往下就涉及汇编代码了,我们不分析具体架构。
回到boot_cpu_init函数接下来执行:
/* Mark the boot cpu "present", "online" etc for SMP and UP case */
set_cpu_online(cpu, true);
set_cpu_active(cpu, true);
set_cpu_present(cpu, true);
set_cpu_possible(cpu, true);
对boot cpu进行一系列的设置:
先看set_cpu_online,这里采用位图对CPU变量进行管理,
linux/kernel/cpu.c
void set_cpu_online(unsigned int cpu, bool online)
{
if (online)
cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
else
cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
}
cpu_online_bits的定义:
linux/kernel/cpu.c
static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;
include/linux/types.h
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
展开后得到
unsigned long cpu_online_bits[BITS_TO_LONGS(CONFIG_NR_CPUS)] __read_mostly;
CONFIG_NR_CPUS is maximum supported processors.
与具体的架构相关。
BITS_TO_LONGS定义在include/linux/bitops.h
#define BITS_PER_BYTE 8
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
include/linux/bitops.h
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
DIV_ROUND_UP表示向上圆整,是一种算法,保证数组的维度不小于nr,并且多出的部分不会太大。
所以这里实际上是定义了一个名为cpu_online_bits,类型为unsigned long的数组。
假设有一个4核CPU,那么CONFIG_NR_CPUS的值为4,而long是32位,sizeof(long)为4,那么DIV_ROUND_UP(4, 8*4)的值为1.即unsigned long bitmap[1],一共32位,足够4个CPU用了。
to_cpumask对cpu_online_bits数组进行转化:
/**
* to_cpumask - convert an NR_CPUS bitmap to a struct cpumask *
* @bitmap: the bitmap
*
* There are a few places where cpumask_var_t isn‘t appropriate and
* static cpumasks must be used (eg. very early boot), yet we don‘t
* expose the definition of ‘struct cpumask‘.
*
* This does the conversion, and can be used as a constant initializer.
*/
#define to_cpumask(bitmap) \
((struct cpumask *)(1 ? (bitmap) : (void *)sizeof(__check_is_bitmap(bitmap))))
其实就是将bitmap转化为struct cpumask *类型。
include/linux/cpumask.h
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
include/linux/threads.h
/* Places which use this should consider cpumask_var_t. */
#define NR_CPUS CONFIG_NR_CPUS
它的成员也是一个unsigned long 类型的数组。
include/linux/cpumask.h
/**
* cpumask_set_cpu - set a cpu in a cpumask
* @cpu: cpu number (< nr_cpu_ids)
* @dstp: the cpumask pointer
*/
static inline void cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp)
{
set_bit(cpumask_check(cpu), cpumask_bits(dstp));
}
接下来就是设置bit位,不同架构对应不同的汇编,我们不准备分析。
以上是对set_cpu_online的分析,其他几个函数也差不多。
这里可以看到,很多cpu相关的变量是用位图形式进行管理的,通过操作位图,可改变cpu的状态。
版权声明:本文为博主原创文章,未经博主允许不得转载。
start_kernel——boot_cpu_init及PER_CPU
标签:
原文地址:http://blog.csdn.net/yin262/article/details/46774041