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. Ruby按照换行符进行分割

    Ruby按照换行符进行分割 string.split(/\n/)

  2. JavaScript AMD规范简单介绍(一)

    AMD是"Asynchronous Module Definition"的缩写.意思就是"异步模块定义". AMD定义了我们所用的模块都是是异步载入的,所以我们 ...

  3. the rendering library is more recent than your version of android studio

    近期更新了自己Android Studio中的SDK到最新版本号,AS的一部分配置改动了. 然后 在打开布局文件的时候 会出现 渲染错误 Rendering problem the rendering ...

  4. POJ-3984-迷宫问题-BFS(广搜)-手写队列

    题目链接:id=3984">http://poj.org/problem? id=3984 这个本来是个模板题,可是老师要去不能用STL里的queue,得自己手写解决.ORZ....看 ...

  5. hdu3966_树链剖分

    近期在强化知识点深度.发现树链剖分不是非常会写了. 回想一下改动操作: 若两个点在同一条链上,则直接改动这段区间. 若不在同一条链上,改动深度较大的点到其链顶端的区间,同一时候将这个点变为他所在链顶端 ...

  6. HttpClient 4.4 请求

    4.2 版本 /** * url 请求 paramUrl * * @time 2015年11月10日下午4:40:22 * @packageName com.rom.utils * @param ur ...

  7. Universal-Image-Loader(UIL)图片载入框架使用简介

    这个也是近期项目中使用到的第三方图片载入框架.在这里也自己总结一下,简单的介绍一些使用的方式. UIL图片载入框架特点 简单介绍: 项目地址:https://github.com/nostra13/A ...

  8. 内存问题检测神器:Valgrind

    Linux下内存问题检测神器:Valgrind 在写大型C/C++工程时难免会发生内存泄漏现象,系统编程中一个重要的方面就是有效地处理与内存相关的问题.你的工作越接近系统,你就需要面对越多的内存问题. ...

  9. Route学习笔记

    前言 UrlRoutingModule.class:这块的代码关联了上一篇中路由部分的一个详细说明 一:Route的讲解 1. 路由模板匹配 添加路由: MapRoute 剔除的路由:IgnoreRo ...

  10. 多线程与MySQL(十)

    1.1 多线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源整合 ...