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的更多相关文章

  1. 第3阶段——内核启动分析之start_kernel初始化函数(5)

    内核启动分析之start_kernel初始化函数(init/main.c) stext函数启动内核后,就开始进入start_kernel初始化各个函数, 下面只是浅尝辄止的描述一下函数的功能,很多函数 ...

  2. Linux移植之内核启动过程start_kernel函数简析

    在Linux移植之内核启动过程引导阶段分析中从arch/arm/kernel/head.S开始分析,最后分析到课start_kernel这个C函数,下面就简单分析下这个函数,因为涉及到Linux的内容 ...

  3. linux源码分析(五)-start_kernel

    前置:这里使用的linux版本是4.8,x86体系. local_irq_disable(); 这个函数是做了关闭中断操作.和后面的local_irq_enable相对应.说明启动的下面函数是不允许被 ...

  4. arm linux kernel启动之start_kernel

    了解完kernel启动以前的汇编之后我们来看看正式的c语言启动代码,也就是我们的start_kernel函数了.start_kernel相当大,里面每一个调用到的函数都足够我们伤脑筋了,我这里只是浅尝 ...

  5. 实验三:gdb跟踪调试内核从start_kernel到init进程启动

    原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 如果我写的不好或者有误的地方请留言 ...

  6. 全局启动函数start_kernel函数注解

    asmlinkage void __init start_kernel(void) { char * command_line; extern struct kernel_param __start_ ...

  7. linux源码分析(三)-start_kernel

    前置:这里使用的linux版本是4.8,x86体系. start_kernel是过了引导阶段,进入到了内核启动阶段的入口.函数在init/main.c中. set_task_stack_end_mag ...

  8. Linux内核启动过程start_kernel分析

    虽然题目是start_kernel分析,但是由于我在ubuntu环境下配置实验环境遇到了一些问题,我觉得有必要把这些问题及其解决办法写下来. 首先我使用的是Ubuntu14.04 amx64,以下的步 ...

  9. arm linux kernel 从入口到start_kernel 的代码分析

    参考资料: <ARM体系结构与编程> <嵌入式Linux应用开发完全手册> Linux_Memory_Address_Mapping http://www.chinaunix. ...

随机推荐

  1. eclipse转Android studio遇到的那些坑

           公司项目有导入10多个libray,还有涉及ndk,转Android studio时碰到不少问题.前后大概花费5个工作日,中间各种奇葩bug,各种编译出错,非常多还有没错误提示.一度想过 ...

  2. 为代码减负之&lt;三&gt;视图(SQL)

    在设计数据库时为了降低数据冗余.一般都会依照三范式去设计,但有时我们在查询时须要通过一字段获取跟这 个字段相关联的好几个字段.可是他们又分布在不同的表中,这时候假设依照正常途径走的话须要同一时候查询好 ...

  3. Android系统编译【转】

    本文转载自;http://blog.csdn.net/zirconsdu/article/details/8005415 Android编译系统分析 概要 由于android编译系统的复杂和使用了不熟 ...

  4. IOC-Castle Windsor映射

    Castle最早在2003年诞生于Apache Avalon项目,目的是为了创建一个IOC(控制反转)框架.发展到现在已经有四个组件了,分别是ActiveRecord(ORM组件),Windsor(I ...

  5. Android Studio 插件 GsonFormat :你还在烦恼 为 Json格式 生成 JavaBean实体类吗?

    在网络层,互联网提供所有应用程序都要使用的两种类型的服务,尽管目前理解这些服务的细节并不重要,但在所有TCP/IP概述中,都不能忽略他们: 无连接分组交付服务(Connectionless Packe ...

  6. Hua Wei 机试题目一

    一.身份证号码验证 题目描述: 我国公民的身份证号码特点如下:1. 长度为18位:2. 第1-17位只能为数字:3. 第18位可以是数字或者小写英文字母x.4. 身份证号码的第7~14位表示持有人生日 ...

  7. JS进阶 - 浏览器工作原理

    一.浏览器的结构 浏览器的主要组件为: 用户界面 - 包括地址栏.前进/后退按钮.书签菜单等.除了浏览器主窗口(显示页面),其他部分都属于用户界面. 浏览器引擎 - 在用户界面和渲染引擎之间传送指令. ...

  8. X Macro

    30年前我念大学时从一个朋友那里学来的一个技巧. 它是汇编语言的一个宏,但很容易转换为C语言宏. 我一直在使用它,但有意思的是我还从没在别人的代码中看到过.现在该我把这个小技巧传递下去了. 让我们举个 ...

  9. @DateTimeFormat无效原因

    一般都是使用@DateTimeFormat把传给后台的时间字符串转成Date,使用@JsonFormat把后台传出的Date转成时间字符串,但是@DateTimeFormat只会在类似@Request ...

  10. 一个完整的Flexbox指南(转载)

    本文由大漠根据Chris Coyier的<A Complete Guide to Flexbox>所译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点.如需转载此 ...