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. hdu 4717 The Moving Points(三分)

    http://acm.hdu.edu.cn/showproblem.php?pid=4717 大致题意:给出每一个点的坐标以及每一个点移动的速度和方向. 问在那一时刻点集中最远的距离在全部时刻的最远距 ...

  2. Android Activity组件的启动过程

    0.总图 1.总图中的第一步,Laucher主线程向ActivityManagerService进程发出START_ACTIVITY_TRANSACTION 如图:第一步 ~/Android/fram ...

  3. 菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t

    菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

  4. BZOJ 4184 线段树+高斯消元

    思路: 线段树表示的是时间 每回最多log个段 区间覆盖 一直到叶子 的线性基 xor 一下 就是答案 一开始没有思路 看了这篇题解 豁然开朗 http://www.cnblogs.com/joyou ...

  5. View的呈现(二)加载流程

    这块涉及到Code+Razor模板=>html[output流] 而这块的问题在于Razor最后生成了什么?--对象:一个类文件:eg:index.cshtml  => index_cst ...

  6. Safari new Date() 兼容问题

    我的时间 var myTime = "2015-12-31 12:10:21"; 正常写法 var  newTime =  new Date(myTime); safari兼容写法 ...

  7. (转)Django学习之 第一章:Django介绍

    Django: Python编程Web框架 如果你上djangoproject.com你会发现对Django的如下解释: “Django is a high-level Python Web fram ...

  8. 页面定制CSS代码初探(六):h2、h3 标题自动生成序号 详细探索过程

    前言 最近在整理博客写作格式的规范,碰到一个问题:标题要不要加序号? 直到我碰到一个人这么说 手动维护编号实在是一件很闹心的事情, 如果位置靠前的某个段落被删除了, 那么几乎每个段落的编号都要手动修改 ...

  9. 路飞学城Python-Day23

    1.计算机基础 Python可以实现各种应用软件,类比word.QQ.爱奇艺等,但是应用这些软件需要计算机硬件, 计算机发展的过程就是人类不断的希望机器去取代人力,解放更多的人力,最终极的理想就是完全 ...

  10. Pyhton学习——Day7

    ##############################################匿名函数################################################## ...