Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How the Linux kernel initializes each module cleverly

2025-02-03 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article analyzes "how the Linux kernel initializes each module cleverly". The content is detailed and easy to understand, the "Linux kernel is how ingenious initialization of each module" interested friends can follow the editor's ideas slowly in-depth to read, I hope that after reading can be helpful to everyone. Let's learn more about how the Linux kernel initializes each module cleverly with the editor.

I believe that many students who are studying the linux kernel source code will often find that the initialization functions of some modules cannot find callers, such as the initialization functions of the following network modules:

/ / net/ipv4/af_inet.cstatic int _ init inet_init (void) {. / * * Set the IP module up * / ip_init (); / * Setup TCP slab cache for open requests. * / tcp_init (); / * Setup UDP memory threshold * / udp_init ();...} fs_initcall (inet_init)

Even if you search the entire kernel code, you can't find anywhere to call this function, so how exactly is this function called?

The secret is in the line of code after this function:

Fs_initcall (inet_init)

In this line of code, fs_initcall is a macro, defined as follows:

/ / include/linux/init.h#define _ _ define_initcall (fn, id, _ sec)\ static initcall_t _ initcall_##fn##id _ _ used\ _ _ attribute__ ((_ _ section__ (# _ sec ".init") = fn ... # define_ _ define_initcall (fn, id) _ define_initcall (fn, id, .initcall # # id)... # define fs_initcall (fn) _ define_initcall (fn, 5)

After the macro is expanded, the result of the above macro call looks something like this:

Static initcall_t _ _ initcall_inet_init5 _ _ attribute__ ((_ _ section__ (".initcall5.init")) = inet_init

As you can see from the above, the fs_ initcall macro ultimately defines a static variable of type initcall_t, and the value is the address of the function represented by the macro parameter.

The definition of the initcall_t type is as follows:

Typedef int (* initcall_t) (void)

As you can see from the above, initcall_t is a function pointer type, and the variable it defines points to a function whose argument is empty and the return type is int.

We can take a look at the inet_init method above, which does meet these requirements.

To sum up, the fs_initcall macro defines a variable _ _ initcall_inet_init5, which is of type initcall_t and whose value is the address of the inet_init function.

At this point, I'm sure many students will think that the linux kernel must call the inet_init function through this variable, right?

Yes and no.

Yes, because the kernel does get the address of the inet_init method and call it through the memory that the variable points to.

No, because the kernel does not access this memory through the _ _ initcall_inet_init5 variable above.

Can you access this memory in other ways without this variable?

Of course, this is the ingenuity of the linux kernel design.

Let's take a look at the definition of the static variable _ _ initcall_inet_init5 after the above macro expansion, in which there is some code as follows:

_ _ attribute__ ((_ _ section__ (".initcall5.init")

This part of the code does not belong to the c language standard, but is an extension of gcc to c, and its purpose is to declare that the variable belongs to the section .initcall5.init.

The so-called section, we can simply understand as a kind of layout and planning of the memory area occupied by the program, such as our common section has .text to store our code, .data or .bss to store our variables.

Through these section definitions, we can put the related functions in the program into the same memory area, so as to facilitate memory management.

In addition to these default section, we can customize the section through gcc's attribute so that we can put related functions or variables into the same section.

For example, the _ _ initcall_inet_init5 variable above belongs to the custom section .initcall5.init.

After defining these section, we can tell linker in the link script what the location and layout of the section looks like in memory.

For x86 platforms, the kernel link script is:

Arch/x86/kernel/vmlinux.lds.S

In this script, section such as .initCall5.init is defined. The specific logic is as follows:

/ / include/asm-generic/vmlinux.lds.h#define INIT_CALLS_LEVEL (level)\ _ _ initcall##level##_start =. \ KEEP (* (.initcall # # level##.init))\ KEEP (* (.initcall # # level##s.init))\ # define INIT_CALLS\ _ _ initcall_start =. \ KEEP (* (.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) \ _ _ initcall_end =.

As you can see from the above, there are many section related to initcall, and .initcall5.init in our example above is just one of them, in addition to .initcall0.init, .initcall1.init, and so on.

These section are defined by the macro init _ CALLS_LEVEL. The section of the same level is put into the same memory area, and the memory areas of the section of different level are connected according to the level size.

For the _ _ initcall_inet_init5 variable above, its section is .initCall5.init, and its level is 5.

Suppose we have another method that calls the macro fs _ initcall, then the static variable defined by the macro belongs to .initcall5.init, and level is also 5.

Since the level of the initcall to which this variable and the _ _ initcall_inet_init5 variable belong is the same, they are continuously placed in the same memory area.

In other words, these static variables with a level of 5 occupy contiguous areas of memory, and because these variables are of type initcall_t, they happen to form an array of type initcall_t, and the starting address of the array is also defined in the INIT_CALLS_LEVEL macro, which is _ _ initcall5_start.

If we want to call these initcall with level 5, we just need to get the _ _ initcall5_start address as the starting address of the array with element type initcall_t, and then iterate through the elements in the array to get the corresponding function pointer, and then we can call the corresponding function through that pointer.

Let's take a look at the specific code:

/ / init/main.cextern initcall_entry_t _ _ initcall_start []; extern initcall_entry_t _ initcall0_start []; extern initcall_entry_t _ initcall1_start []; extern initcall_entry_t _ initcall2_start []; extern initcall_entry_t _ initcall3_start []; extern initcall_entry_t _ initcall4_start []; extern initcall_entry_t _ initcall5_start []; extern initcall_entry_t _ initcall6_start []; extern initcall_entry_t _ initcall7_start [] Extern initcall_entry_t _ _ initcall_end []; static initcall_entry_t * initcall_levels [] _ _ initdata = {_ _ initcall0_start, _ _ initcall1_start, _ _ initcall2_start, _ _ initcall3_start, _ _ initcall4_start, _ _ initcall5_start, _ _ initcall6_start, _ _ initcall7_start, _ _ initcall_end,} Static void _ _ init do_initcall_level (int level) {initcall_entry_t * fn;... For (fn = initcall_ levels]; fn for (level = 0; level)

In the above code, the do_initcalls method iterates through all the legal level, and for each level,do_initcall_level method, it calls all the functions pointed to by the function pointer in that level.

The inet_init method in our example above belongs to level 5 and is also called here.

The linux kernel invokes the initialization methods of each module in this way, isn't it clever?

Finally, let's sum up:

\ 1. After each module's initialization method, a macro similar to fs_initcall (inet_init) is usually called, whose argument is the method name of the module's initialization method.

\ 2. The result of the expansion of the macro is to define a static variable that declares the section of the initcall level to which it belongs through the attribute of gcc. For example, the static variable corresponding to the inet_init method belongs to the section of .initcall5.init.

\ 3. In linux's link script, linker is told through the INIT_CALLS_LEVEL macro that all static variables belonging to the same level are placed in a continuous block of memory to form an array of element type initcall_t, with the starting address of the array in a variable like _ _ initcall5_start.

\ 4. In the initialization process of the kernel, the function pointer in each level is traversed by calling the do_initcalls method, and then the method that the pointer points to is called, that is, the initialization method of each module.

What is Linux system Linux is a free-to-use and free-spread UNIX-like operating system, is a POSIX-based multi-user, multi-task, multi-threaded and multi-CPU operating system, using Linux can run major Unix tools, applications and network protocols.

On the Linux kernel is how ingenious initialization of each module to share here, I hope that the above content can make you improve. If you want to learn more knowledge, please pay more attention to the editor's updates. Thank you for following the website!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report