zoukankan      html  css  js  c++  java
  • start_kernel——boot_cpu_init及PER_CPU

    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
    ",
                preempt_count() - 1, current->comm, current->pid);
        print_symbol("caller is %s
    ", (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,而long32位,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的状态。

  • 相关阅读:
    Windows安全应急响应(一)
    net.exe和net1.exe的区别&联系.
    IIS搭建ASP站点
    在linux中安装VM tools
    第十五章 特殊权限
    第十四章 权限管理
    第十三章 用户组与提权
    第十一章 用户的创建
    第十章 组的创建
    第八章 vim 编辑器
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7221520.html
Copyright © 2011-2022 走看看