Linux Kernel module analysis (module_init macros) __linux

Source: Internet
Author: User

When we learn about Linux driver development, we first need to understand the modularity mechanism (module) of Linux, but module is not only used to support driver loading and unloading. One of the simplest examples of modules is as follows:

FILENAME:HELLOWORLD.C

#include <linux/module.h>
#include <linux/init.h>

static int Hello _init (void)
{
    printk (kern_alert "Hello world\n");
    return 0;
}

static void Hello_exit (void)
{
    printk (kern_alert "Bye Bye world\n");

Module_init (hello_init);
Module_exit (hello_exit);
Module_license ("Dual BSD/GPL");

The module code has two kinds of operation Way, one is the static compilation joins into the kernel, carries on the initialization in the system initiation process, one is compiles the module which can dynamically load, through Insmod dynamic loading to locate to the kernel. Both of these methods can be selected in makefile through the obj-y or obj-m options.
  
Once the dynamically loaded module target code (. ko) is added to the kernel, its scope and statically linked code are completely equivalent . So the advantages of this operation is obvious: You can run the dynamic loading module according to the system needs to expand the kernel function and uninstall it when it is not needed to free up memory space; When you need to modify kernel functionality, you simply compile the module without recompiling the entire kernel.

Because of this advantage, in the device-driven development, basically is to compile it into a dynamically loaded module. Note, however, that some modules must be compiled into the kernel, run with the kernel, never unloaded, such as VFS, Platform_bus, and so on.

So how does a C code implement both of these two ways?
The answer lies in module_init macros. Now let's analyze the Module_init macros together. (The Linux kernel version used here is 3.10.10)
To locate the include/linux/init.hin the Linux kernel source code, you can see the following:

#ifndef MODULE
//ellipsis
#define MODULE_INIT (x)  __initcall (x);
Omit
#else

#define MODULE_INIT (INITFN) \
    int init_module (void) __attribute__ ((alias (#initfn));
Omitted
#endif

Obviously, MODULE is controlled by makefile. The above section is used to connect the module static compilation into the kernel, and the following sections are used to compile dynamically loaded modules. Next we analyze both of these situations.

Mode one: #ifndef MODULE

Code Grooming:

#define MODULE_INIT (x)  __initcall (x);
|
--> #define __initcall (FN) Device_initcall (FN)
    |
    --> #define Device_initcall (FN)     __define_initcall (FN, 6)
        |
        --> #define __define_initcall (FN, id) \
                static initcall_t __initcall_# #fn # #id __used \
                __attribute__ (__ Section__ (". Initcall" #id ". Init")) = fn

That is, module_init (hello_init) expands to:

static initcall_t __initcall_hello_init6 __used \
    __attribute__ ((__section__ (". Initcall6.init")) = Hello_init

The initcall_t here is the function pointer type, as follows:

typedef int (*initcall_t) (void);

The GNU compilation tool chain supports user-defined section, so when we read the Linux source code, we see a lot of use of the following categories:

__ATTRIBUTE__ is used to specify a specific property of a variable or struct bit field, followed by a description of the property in the parentheses, which is in the syntax format: __attribute__ ((attribute-list)). It has a position constraint, usually placed at the end of the declaration and preceded by ";".
The attribute-list here is __section__ (". Initcall6.init"). Typically, the compiler stores the generated code in a. Text section. But sometimes you may need other segments, or you need to store certain functions and variables in a particular segment, and the section property is used to specify that a function or variable be stored in a particular segment.

So here's what this means: Define a function pointer variable named __INITCALL_HELLO_INIT6 and initialize it to Hello_init (pointing to Hello_init), and the function pointer variable is stored in the. Initcall6.init code snippet.

Next, we'll look at the. Initcall6.init segment by looking at the link script ( arch/$ (arch)/kernel/vmlinux.lds.s).
As you can see, the. INIT segment contains Init_calls, which is defined in Include/asm-generic/vmlinux.lds.h. Init_calls after expansion can be:

#define Init_calls                          \
        vmlinux_symbol (__initcall_start) =.;           \
        * (. initcallearly.init)                  \
        init_calls_level (0)                 \
        init_calls_level (1)                 \
        Init_calls_ Level (2)                 \
        init_calls_level (3)                 \
        init_calls_level (4)                 \
        init_calls_level (5)                 \
        init_calls_level (rootfs)                \
        init_calls_level (6)                 \
        init_calls_level (7)                 \
        Vmlinux_symbol (__initcall_end) =.;

Further expanded to:

        __initcall_start =.;           \
        * (. initcallearly.init)          \
        __initcall0_start =.;          \
        * (. initcall0.init)              \
        * (. initcall0s.init)             \/
        /omit 1, 2, 3, 4, 5
        __initcallrootfs_start =.;     \
        * (. initcallrootfs.init)         \
        * (. initcallrootfss.init)            \
        __initcall6_start =.;          \
        * (. initcall6.init)              \
        * (. initcall6s.init)             \
        __initcall7_start =.;          \
        * (. initcall7.init)              \
        * (. initcall7s.init)             \
        __initcall_end =.;

The above code snippets are ultimately organized sequentially in Kernel.img, which determines the order in which some functions are performed (__INITCALL_HELLO_INIT6 is in the. Initcall6.init segment). Init or. The feature of the Initcalls segment is that the memory in this segment is released when the kernel is started. This can be seen from the kernel boot information:

Freeing unused kernel memory:124k (80312000-80331000)

So how did the __initcall_hello_init6 in the. Initcall6.init paragraph be invoked? We look at the file init/main.c, the code is sorted as follows:

Start_kernel
|
--> Rest_init
    |
    --> Kernel_thread
        |
        --> Kernel_init
            |
            --> kernel_init_freeable
                |
                --> Do_basic_setup
                    |
                    --> Do_initcalls
                        |
                        --> Do_initcall_level (level)
                            |
                            --> Do_one_initcall (initcall_t fn)

Kernel_init This function is invoked as a kernel thread (the thread will eventually start the first user process init).
We focus on the do_initcalls function as follows:

static void __init do_initcalls (void)
{
    int level;

    for [level = 0, Level < Array_size (initcall_levels)-1; level++)
        do_initcall_level (level);
}

The function do_initcall_level is as follows:

static void __init do_initcall_level (int level)
{
    //ellipsis for
    (fn = Initcall_levels[level]; FN < Initcall_le VELS[LEVEL+1]; fn++)
        Do_one_initcall (*FN);
}

The function Do_one_initcall is as follows:

int __init_or_module Do_one_initcall (initcall_t fn)
{
    int ret;
    Omit
    ret = fn ();
    return ret;
}

The definition of Initcall_levels is as follows:

Static initcall_t *initcall_levels[] __initdata = {
    __initcall0_start,
    __initcall1_start,
    __ Initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_ Start,
    __initcall7_start,
    __initcall_end,
};

The members of initcall_levels[] come from the unfolding of the init_calls, such as "__initcall0_start =.;", where the __initcall0_start is a variable that is the same as the variables defined inside the code, So the code can use __initcall0_start. Therefore, in init/main.c, these variables can be introduced by means of extern, as follows:

extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];

This basically makes it clear that in the do_initcalls function, each function pointer in the initcalls segment is traversed, and then the function pointer executes. Because the compiler links each function pointer to a specified location based on the requirements of the link script, you can safely perform the associated initialization function with Do_one_initcall (*FN).
  
Our example module_init (hello_init) is the Level6 initcalls segment, compared to the later call, many peripheral drivers call Module_init macros, if the static compilation is connected into the kernel, These function pointers are inserted into the initcall6.init segment in the compiled order, and then wait for the do_initcalls function call.

Mode two: #else

Related code:

#define MODULE_INIT (INITFN)                 \
    static inline initcall_t __inittest (void)       \
    {return initfn;}                  \
    int init_module (void) __attribute__ ((alias (#initfn));

  __inittest is simply to detect if the defined function conforms to the initcall_t type, and if it is not the __inittest type, the error occurs at compile time. So the real macro definition is:

#define MODULE_INIT (INITFN)                 \
    int init_module (void) __attribute__ ((alias (#initfn));

Therefore, using dynamic loading, you can not use Module_init and Module_exit macros, and directly define the Init_module and cleanup_module functions, the effect is the same.
  
  The alias property is a unique property of GCC that defines init_module as the alias for the function Initfn. So the role of Module_init (Hello_init) is to define a variable name Init_module, whose address and Hello_init are the same.
  
The above example compiles the dynamic load module process, will automatically produce the Helloworld.mod.c file, the contents are as follows:

#include <linux/module.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>

Module_info (Vermagic, vermagic_string);

struct Module __this_module
__attribute__ ((Section (". Gnu.linkonce.this_module")) = {
    . Name = Kbuild_modname ,
    . init = init_module,
#ifdef config_module_unload
    . Exit = Cleanup_module,
#endif
    . Arch = module _arch_init,
};

static const char __module_depends[]
__used
__attribute__ ((Section (". Modinfo")) =
"depends=";

It defines a global variable __this_moduleof type module, member init is Init_module (that is, hello_init), and the variable is linked to . in the Gnu.linkonce.this_module section.

The compiled Helloworld.ko need to be loaded into the kernel through insmod , because Insmod is the user layer command provided by BusyBox, so we need to read the BusyBox source code. The code is combed as follows: (file busybox/modutils/insmod.c)

Insmod_main
|
--> Bb_init_module
    |
    --> Init_module

The init_module definition is as follows: (file busybox/modutils/modutils.c)

#define Init_module (mod, Len, opts) syscall (__nr_init_module, mod, Len, opts)

Therefore, the system calls the Sys_init_module function of the corresponding kernel layer.

Back to the Linux kernel source code (kernel/module.c), code grooming:

Syscall_define3 (Init_module, ...)
|
-->load_module
    |
    --> do_init_module (mod)
        |
        --> Do_one_initcall (Mod->init);

In the document (include/linux/syscalls.h), there are:

#define SYSCALL_DEFINE3 (name, ...) Syscall_definex (3, _# #name, __va_args__)

Thus forming the sys_init_module function.

This concludes the analysis. Mistakes and omissions, but also hope that readers point out.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.