"Just as you need to include the header file of C library when writing C programs, Linux kernel programming also needs to include the kernel header file. Most linux drivers need to include the following three header files:
#include
#include
#include
Among them, init. H defines the functions related to the initialization and exit of the driver, kernel. H defines the frequently used function prototype and macro definitions, and module. H defines the functions, variables and macros related to the kernel module.
Almost every linux driver has a module_ Init (and module)_ Exit is defined in init. H (/ include / Linux) Medium). Yes, the loading of the driver depends on it. Why do I need such a macro? The reason is that according to the general programming idea, the initialization functions of each part will be called in a fixed function, such as:
void init(void)
{
init_ a();
init_ b();
}
If you add another initialization function, then in init_ Add a line after B (): init_ c(); This can really complete our functions, but there is a certain problem, that is, we can't add initialization functions independently. Each time we add a new function, we have to modify the init function. You can deal with this problem in another way, just decorate it with a macro:
void init_ a(void)
{
}
__ initlist(init_ a, 1);
How does it initialize the function list through this macro? Let's see first__ Definition of initlist:
[cpp] view plain copy
#define __ init __ attribute__(( unused, __ section__("". initlist"")))
#define __ initlist(fn, lvl) /
staTIc initlist_ t __ init_## fn __ init = { /
magic: INIT_ MAGIC, /
callback: fn, /
level: lvl }
Please note:__ secTIon__("". Initlist ''), what does this attribute do? It tells the connector that this variable is stored in the. Initlist section. If all initialization functions use this macro, each function will have a corresponding initlist_ T structure variables are stored in the. Initlist section, that is, we can find the pointers of all initialization functions in the. Initlist section. How do I find the address of the. Initlist section?
extern u32 __ initlist_ start;
extern u32 __ initlist_ end;
These two variables work__ initlist_ Start is the beginning of the. Initlist section__ initlist_ End is the end. Through these two variables, we can access all initialization functions. Where are these two variables defined? In a connector script file
[cpp] view plain copy
. = ALIGN(4);
.initlist : {
__ initlist_ start = .;
*(.initlist)
__ initlist_ end = .;
}
The values of these two variables are exactly defined at the start and end addresses of the. Initlist section, so we can access all initialization functions through these two variables.
Similarly, this method is also used in the kernel, so we write the driver independently. We don't need to add our own code to call our own initialization function and exit function in a fixed place. The connector has been prepared for us. Let's analyze module first_ init。 It is defined as follows:
[cpp] view plain copy
#define module_ init(x) __ initcall(x); // include/linux/init.h
#define __ initcall(fn) device_ initcall(fn)
#define device_ initcall(fn) __ define_ initcall(""6"",fn,6)
#define __ define_ initcall(level,fn,id) /
staTIc initcall_ t __ initcall_## fn##id __ used /
__ attribute__((__ secTIon__("". initcall"" level "". init""))) = fn
If a driver wants to use func as the entry of the driver, it can be declared as follows: module_ init(func); After being processed by the above macro, it becomes__ initcall_ func6 __ Used added to the ". Initcall" section of the kernel image. When the kernel is loaded, all entries in ". Initcall" will be searched and loaded according to their priority. The priority of ordinary drivers is 6. Other module priorities are listed as follows: the smaller the value, the first to load.
[cpp] view plain copy
#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)
As you can see, it is declared pure_ Initcall is loaded first.
module_ In addition to initializing loading, init also releases memory in the later stage. A large part of the code in the Linux kernel is device driver code. These driver codes have initialization and de initialization functions. These codes are generally executed only once. In order to make more effective use of memory, the memory occupied by these codes can be released.
This is what Linux does, adding to all functions that only need to be initialized and run once__ Init attribute__ The init macro tells the compiler to put the function into the (. Init. Text) section and module if the module is compiled into the kernel_ The parameters of exit are the same when uninstalling__ Similar to init, if the driver is compiled into the kernel, then__ The exit macro ignores the cleanup function because the modules compiled into the kernel do not need to be cleaned up. Obviously__ Init and__ Exit is not valid for dynamically loaded modules. It only supports full compilation into the kernel.
After kernel initialization, the memory space occupied by all these function codes is released. Connector strap__ The function of init attribute is placed in the same section. After it is used up, the whole section is released. After function initialization, this area can be cleared to save system memory. The message "freeing unused kernel memory: xxxk free" kenrel sees when starting is related to it.
Let's look at the source code, start in init / main. C_ Kernel is the first c function to enter kernel (), and the last line of this function is rest_ init();
static void rest_ init(void)
{
.....
kernel_ thread(kernel_ init, NULL, CLONE_ FS | CLONE_ SIGHAND);
unlock_ kernel();
cpu_ idle();
.....
}
Created a kernel thread, the main function kernel_ There is a function at the end of init:
[cpp] view plain copy
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
init_ post();
This init_ The first sentence in post is free_ initmem(); It is used to release initialization code and data.
[cpp] view plain copy
void free_ initmem(void)
{
if (! machine_ is_ integrator() && ! machine_ is_ cintegrator()) {
free_ area((unsigned long)(&__ init_ begin),
(unsigned long)(&__ init_ end),
""init"");
}
}
Next is kernel memory management., Read the full text“
Our other product: