Follow+Starred PublicNo., don’t miss exciting content
Source | Embedded Hodgepodge
Usually when we write programs, we follow this routine. Function after function is executed one by one according to sequential logic.
If the logic is very complex and involves many modules, then the code executed sequentially will be bloated and the modules will be very tightly coupled. There are various peripheral drivers in the Linux kernel, and it is almost impossible to execute them logically in a sequence.
The kenrel code can have such a large amount of code, it is large but not messy, it effectively separates each level and each module, and a large amount of code is logically organized together, and this initcall plays a vital role.
By imitating this method, we finally clear the main function code in the picture, separate this logic, and achieve the same function.
How to implement such a function requires some background knowledge:
1. Organization of program code
2. Knowledge related to link scripts.
3. Application of function pointers.
1. The organization of the code. For example, in the picture, you need to know which sections of the program the variables a, b and function pointers f and f2 are stored in. You can take a look at this stm32 startup code implementation | C language. The above a and f are all Stored in the bss segment, b, f2 are stored in the data segment, because the initial value has been given, and implementing this intcall will put the data that needs to be automatically initialized into a custom segment, such as .initcall.
How to put it in a specific section, you need to use the attribute((section)) keyword to change the data storage section.
The current program is compiled using these segments. Except for .isr_vector, which is also added, the others are the default ones of the compiler.
First add some code:
Of course, this is not enough. You also need to tell the linker (LD) to link the .initcall section into the program, so this section also needs to be modified.
This section is aligned by 8 bytes, two global variables are defined, and these data are linked in order 0-5. With these two modifications, let's take a look at each section of the program.
As picture:
There is an extra red box for the .initcalls section. This section is 8 bytes in total, starting from 0x80005a8.
Let's take a look at the specific situation of this paragraph, using the readelf tool.
It matches the size tool above, and the address of the green box is SystemInit (0x08000231, little endian mode.)
So through attribute and modifying the link script, the function pointer variable is placed in the .initcall section.
So how to call this function is similar to the previous initialization data segment data. It traverses this segment, then takes out the function address, and then forcibly converts the address in the segment into a function pointer, and then calls it directly.
The picture implemented is to take the function address from the .initcall section and then call it directly. It is very easy to confuse the address of the function with the address of the function pointer variable.
With the code modified like this, the automatic initialization function can indeed be adjusted, but every time I have to write such a long section of static initcall_t __ attribute__(( __ used__,__ section__(".initcall.0.init"))), that is Uncomfortable. Modified through macros in the Linux kernel.
Same goes for this one.
Add some macros that are executed in the logical order of the program
0, low_level_init, such as initializing the system basic clock
1. arch_init, for example, put some initialization of the CPU architecture such as initializing NVIC.
2. dev_init initializes peripheral modules, such as i2c, flash, spi, etc.
3. board_init makes some settings for specific hardware boards.
4. Some settings of os_init operating system, such as file system, network protocol stack, etc.
5. app_init finally runs the user program.
Modify your own program and use macros instead. In this way, the call to do_initcalls will be executed in the order of 0, 1-to 5.
Finally, let’s take a look at the initcall section:
In this way, just add something like dev_init(), app_init() to the automatic initialization function, and it will be called automatically, without the need to execute them one by one in the main function.
For example, the initialization of i2c control is placed in dev_init. There are many i2c slave devices hanging below. You only need to initialize each slave device with app_init. Even if a new one comes, just use this app_init to initialize it. There is no need to change the original one. , a high degree of coupling between separated modules.
In this way, Linux kenerl is simulated, initialization and verification are successful, and finally uploaded to the library.
https://gitee.com/android_life/stm32_freertos_opensource/tree/master/bareos/initcall
If you are interested, you can take a look, follow and forward.
Original text: https://www.toutiao.com/a6906858875912307203/?log_from=16f51be0430a9_1647178471075
Statement:The material of this article comes from the Internet, and the copyright belongs to the original author. If there is any copyright issue with the work, please contact me to delete it.
------------ END ------------
●Column "Embedded Development"
●Embedded column selected tutorials
Follow the public account and reply "Join the group" Join the technical exchange group according to the rules and reply "1024 " See more.
Click "Read the original text" to view more shares.