• 内核initcall分析


    linux中init相关内容定义在include/linux/init.h

    1. initcall相关定义

    先看下文件说明,此文件定义的宏主要用于初始化阶段标记函数或初始化数据,之后占用的资源会被释放掉。

    /* These macros are used to mark some functions or

     * initialized data (doesn't apply to uninitialized data)

     * as `initialization' functions. The kernel can take this

     * as hint that the function is used only during the initialization

     * phase and free up used memory resources after

     *

     * Usage:

     * For functions:

     *

     * You should add __init immediately before the function name, like:

     *

     * static void __init initme(int x, int y)

     * {

     *    extern int z; z = x * y;

     * }

     *

     * If the function has a prototype somewhere, you can also add

     * __init between closing brace of the prototype and semicolon:

     *

     * extern int initialize_foobar_device(int, int, int) __init;

     *

     * For initialized data:

     * You should insert __initdata between the variable name and equal

     * sign followed by value, e.g.:

     *

     * static int init_variable __initdata = 0;

     * static char linux_logo[] __initdata = { 0x32, 0x36, ... };

     *

     * Don't forget to initialize data not at file scope, i.e. within a function,

     * as gcc otherwise puts the data into the bss section and not into the init

     * section.

     *

     * Also note, that this data cannot be "const".

     */

    在内核中经常会看到arch_initcall()、subsys_initcall()、device_initcall()等,定义均在include/linux/init.h中:

    #define pure_initcall(fn)       __define_initcall("0",fn,0)

    #define core_initcall(fn)       __define_initcall("1",fn,1)

    #define core_initcall_sync(fn)      __define_initcall("1s",fn,1s)

    #define postcore_initcall(fn)       __define_initcall("2",fn,2)

    #define postcore_initcall_sync(fn)  __define_initcall("2s",fn,2s)

    #define arch_initcall(fn)       __define_initcall("3",fn,3)

    #define arch_initcall_sync(fn)      __define_initcall("3s",fn,3s)

    #define subsys_initcall(fn)     __define_initcall("4",fn,4)

    #define subsys_initcall_sync(fn)    __define_initcall("4s",fn,4s)

    #define fs_initcall(fn)         __define_initcall("5",fn,5)

    #define fs_initcall_sync(fn)        __define_initcall("5s",fn,5s)

    #define rootfs_initcall(fn)     __define_initcall("rootfs",fn,rootfs)

    #define device_initcall(fn)     __define_initcall("6",fn,6)

    #define device_initcall_sync(fn)    __define_initcall("6s",fn,6s)

    #define late_initcall(fn)       __define_initcall("7",fn,7)

    #define late_initcall_sync(fn)      __define_initcall("7s",fn,7s)

    #define __initcall(fn) device_initcall(fn)

    __define_initcall()宏定义:

    /* initcalls are now grouped by functionality into separate

     * subsections. Ordering inside the subsections is determined

     * by link order.

     * For backwards compatibility, initcall() puts the call in

     * the device init subsection.

     *

     * The `id' arg to __define_initcall() is needed so that multiple initcalls

     */

    #define __define_initcall(level,fn,id)

        static initcall_t __initcall_##fn##id __used

        __attribute__((__section__(".initcall" level ".init"))) = fn

    typedef int (*initcall_t)(void);

    typedef void (*exitcall_t)(void);

    *_initcall(fn)最终都是通过__define_initcall(level,fn,id)宏定义生成,而最终所有的initcall_t型函数都存放在.initcall”level”.init section中。.initcall”level”.init定义在vmlinux.lds内。

    /* arch/arm/kernel/vmlinux.lds */

    __initcall_start = .;

       *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)

      __initcall_end = .;

    正好包括了上面init.h里定义的从pure_initcall到late_initcall等8个level等级的.initcall”level”.init section. 因此通过不同的*_initcall声明的函数指针最终都会存放不同level等级的.initcall”level”.init section内。这些不同level的section按level等级高低依次存放。

    附:模块相关定义

    内核内置模块定义

    /**

     * module_init() - driver initialization entry point

     * @x: function to be run at kernel boot time or module insertion

     *

     * module_init() will either be called during do_initcalls() (if

     * builtin) or at module insertion time (if a module).  There can only

     * be one per module.

     */

    #define module_init(x)  __initcall(x);

    /**

     * module_exit() - driver exit entry point

     * @x: function to be run when driver is removed

     *

     * module_exit() will wrap the driver clean-up code

     * with cleanup_module() when used with rmmod when

     * the driver is a module.  If the driver is statically

     * compiled into the kernel, module_exit() has no effect.

     * There can only be one per module.

     */

    #define module_exit(x)  __exitcall(x);

    单独模块定义#define MODULE

    /* Each module must use one module_init(). */

    #define module_init(initfn)                

        static inline initcall_t __inittest(void)      

        { return initfn; }                 

        int init_module(void) __attribute__((alias(#initfn)));

    /* This is only required if you want to be unloadable. */

    #define module_exit(exitfn)                 

        static inline exitcall_t __exittest(void)      

        { return exitfn; }                 

        void cleanup_module(void) __attribute__((alias(#exitfn)));

    #define __setup_param(str, unique_id, fn)   /* nothing */

    #define __setup(str, func)          /* nothing */

    2.initcall相关调用

    内核是通过do_initcalls函数循环调用执行initcall.init section内的函数的,流程如下(init/main.c):

    start_kernel -> rest_init -> kernel_thread -> kernel_init -> do_basic_setup -> do_initcalls

    kernel_thread创建一个内核线程执行kernel_init函数(linux的1号进程-init进程)

    extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];

    static void __init do_initcalls(void)

    {

        initcall_t *call;

        for (call = __early_initcall_end; call < __initcall_end; call++)

            do_one_initcall(*call);

        /* Make sure there is no pending stuff from the initcall sequence */

        flush_scheduled_work();

    }

    do_initcalls()遍历.initcall*.init段,依次执行各个级别的函数。

    最后要注意的是rest_init是在start_kernel函数内最后部分才被调用执行的,rest_init前包含了kernel一系列的初始化工作。另外,这些不同level等级的initcall.init section本身有一定的执行顺序,因此如果你的驱动依赖于特定的执行顺序的话需要考虑到这一点。

    参考:http://linux.chinaunix.net/techdoc/develop/2008/07/19/1018489.shtml

  • 相关阅读:
    1 step / 2 step STO
    关于Shipping
    Retail Article Master Data 学习 (四) Assortment
    SAP Basis 性能分析 记忆点
    从数据库中读取二进制数据,并显示图片
    [转]c#金山词霸组件法
    C# 处理INI文个类 INIManager
    C#实现通过HttpWebRequest发送POST请求实现网站自动登陆
    XPath语法参考
    关于IE6和IE7关闭窗口时提示和不提示以及上传图片前的本地预览解决办法
  • 原文地址:https://www.cnblogs.com/embedded-linux/p/5885127.html
Copyright © 2020-2023  润新知