Now we are still wandering about the machine code. Yesterday we analyzed the code in arch/x86/kernel/head_32.s. The era of Assembly has passed. Now, all the code is C, nice. We are getting closer and closer to the base camp, and the device model is not far ahead. However, we haven't analyzed it yet. It is estimated that you are still confused. Where is it? Haha, soon, it will all be done in an instant. Don't miss too much boring things too much. Now it's time to jump to what we said yesterday.
From ARCH/x86/kernel/head32.c:
Void _ init i1__start_kernel (void)
{
# Ifdef config_x86_trampoline
/*
* But first pinch a few for the stack/trampoline stuff
* Fixme: don't need the extra page at 4 K, but need to fix
* Trampoline before removing it. (See the gdt stuff)
*/
Reserve_early_overlap_ OK (page_size, page_size + page_size,
"Ex trampoline ");
# Endif
Reserve_early (_ pa_symbol (& _ text), _ pa_symbol (& __ bss_stop), "text data BSS ");
# Ifdef config_blk_dev_initrd
/* Reserve initrd */
If (boot_params.hdr.type_of_loader & boot_params.hdr.ramdisk_image ){
/* Assume only end is not page aligned */
U64 ramdisk_image = boot_params.hdr.ramdisk_image;
U64 ramdisk_size = boot_params.hdr.ramdisk_size;
U64 ramdisk_end = page_align (ramdisk_image + ramdisk_size );
Reserve_early (ramdisk_image, ramdisk_end, "ramdisk ");
}
# Endif
/* Call the subarch specific early setup function */
Switch (boot_params.hdr.hardware_subarch ){
Case x86_subarch_mrst:
X86_mrst_early_setup ();
Break;
Default:
I __default_early_setup ();
Break;
}
/*
* At this point everything still needed from the boot loader
* Or BIOS or kernel text shoshould be early reserved or marked not
* RAM in e820. all other memory is free game.
*/
# In fact, it doesn't matter if we don't understand the above part, because we can't touch things that are too low-level. After all, the content is too complicated, not at our age.
# It can be tricky. We still need to accumulate a lot of experience to cope with it, and our base camp is not far ahead.
Start_kernel ();
}
Start_kernel function, which indicates that we are about to fly out of the machine-related part, to a very free and unrestrained place-machine-independent code.
From init/Main. C:
Asmlinkage void _ init start_kernel (void)
{
Char * command_line;
Extern const struct kernel_param _ start ___ Param [], _ Stop ___ Param [];
Smp_setup_processor_id ();
/*
* Need to run as early as possible, to initialize
* Lockdep hash:
*/
Lockdep_init ();
Debug_objects_early_init ();
/*
* Set up the initial canary ASAP:
*/
Boot_init_stack_canary ();
Cgroup_init_early ();
Local_irq_disable ();
Early_boot_irqs_off ();
Early_init_irq_lock_class ();
/*
* Interrupts are still disabled. Do necessary setups, then
* Enable them
*/
Tick_init ();
Boot_cpu_init ();
Page_address_init ();
Printk (kern_notice "% s", linux_banner );
Setup_arch (& command_line );
Mm_init_owner (& init_mm, & init_task );
Setup_command_line (command_line );
Setup_nr_cpu_ids ();
Setup_per_cpu_areas ();
Smp_prepare_boot_cpu ();/* arch-specific boot-CPU hooks */
Build_all_zonelists (null );
Page_alloc_init ();
Printk (kern_notice "kernel command line: % s/n", boot_command_line );
Parse_early_param ();
Parse_args ("booting kernel", static_command_line, _ start ___ Param,
_ Stop ___ param-_ start ___ Param,
& Unknown_bootoption );
/*
* These use large bootmem allocations and must precede
* Kmem_cache_init ()
*/
Pidhash_init ();
Vfs_caches_init_early ();
Sort_main_extable ();
Trap_init ();
Mm_init ();
/*
* Set up the scheduler prior starting any interrupts (such as
* Timer interrupt). Full topology setup happens at smp_init ()
* Time-but meanwhile we still have a functioning scheding.
*/
Sched_init ();
/*
* Disable preemption-early bootup scheduling is extremely
* Fragile until we cpu_idle () for the first time.
*/
Preempt_disable ();
If (! Irqs_disabled ()){
Printk (kern_warning "start_kernel (): Bug: interrupts were"
"Enabled * very * early, fixing it/N ");
Local_irq_disable ();
}
Rcu_init ();
Radix_tree_init ();
/* Init some links before init_isa_irqs ()*/
Early_irq_init ();
Init_irq ();
Prio_tree_init ();
Init_timers ();
Hrtimers_init ();
Softirq_init ();
Timekeeping_init ();
Time_init ();
Profile_init ();
If (! Irqs_disabled ())
Printk (kern_crit "start_kernel (): Bug: interrupts were"
"Enabled early/N ");
Early_boot_irqs_on ();
Local_irq_enable ();
/* Interrupts are enabled now so all green allocations are safe .*/
Gfp_allowed_mask = _ gfp_bits_mask;
Kmem_cache_init_late ();
/*
* Hack alert! This is early. We're re enabling the console before
* We 've done PCI setups etc, and console_init () must be aware
* This. But we do want output early, in case something goes wrong.
*/
Lele_init ();
If (panic_later)
Panic (panic_later, panic_param );
Lockdep_info ();
/*
* Need to run this when irqs are enabled, because it wants
* To self-test [Hard/soft]-irqs On/Off lock inversion bugs
* Too:
*/
Locking_selftest ();
# Ifdef config_blk_dev_initrd
If (initrd_start &&! Initrd_below_start_ OK &&
Page_to_pfn (pai_to_page (void *) initrd_start) <min_low_pfn ){
Printk (kern_crit "initrd overwritten (0x % 08lx <0x % 08lx )-"
"Disabling it./N ",
Page_to_pfn (pai_to_page (void *) initrd_start )),
Min_low_pfn );
Initrd_start = 0;
}
# Endif
Page_cgroup_init ();
Enable_debug_pagealloc ();
Kmemleak_init ();
Debug_objects_mem_init ();
Idr_init_cache ();
Setup_per_cpu_pageset ();
Numa_policy_init ();
If (late_time_init)
Late_time_init ();
Sched_clock_init ();
Calibrate_delay ();
Pidmap_init ();
Anon_vma_init ();
# Ifdef config_x86
If (efi_enabled)
Efi_enter_virtual_mode ();
# Endif
Thread_info_cache_init ();
Cred_init ();
Fork_init (totalram_pages );
Proc_caches_init ();
Buffer_init ();
Key_init ();
Security_init ();
Dbg_late_init ();
Vfs_caches_init (totalram_pages );
Signals_init ();
/* Rootfs populating might need page-writeback */
Page_writeback_init ();
# Ifdef config_proc_fs
Proc_root_init ();
# Endif
Cgroup_init ();
Cpuset_init ();
Taskstats_init_early ();
Delayacct_init ();
Check_bugs ();
Acpi_early_init ();/* Before lapic and SMP init */
Sfi_init_late ();
Ftrace_init ();
/* Do the rest non-_ init 'ed, we're now alive */
# If you see none of the above, it's basically xxx_init (). I'm happy! When I was so stupid that I wouldn't be able to take a look at it one by one,
# We are not old enough
Rest_init ();
}
Jump to the rest_init function:
Static noinline void _ init_refok rest_init (void)
_ Releases (kernel_lock)
{
Int PID;
# RCU is really disgusting.
Rcu_scheduler_starting ();
/*
* We Need to spawn init first so that it obtains PID 1, however
* The init task will end up wanting to create kthreads, which, if
* We schedule it before we create kthreadd, Will oops.
*/
# This function can be used as the name suggests. To put it bluntly, it is the kernel thread.
# The thread function is kernel_init.
# For more information about the process and thread concepts, see the books. We will not cover process management.
# You only need to know the function usage
Kernel_thread (kernel_init, null, clone_fs | clone_sighand );
Numa_default_policy ();
# The following can be ignored directly
PID = kernel_thread (kthreadd, null, clone_fs | clone_files );
Rcu_read_lock ();
Kthreadd_task = find_task_by_pid_ns (PID, & init_pid_ns );
Rcu_read_unlock ();
Complete (& kthreadd_done );
/*
* The Boot idle thread must execute schedule ()
* At least once to get things moving:
*/
Init_idle_bootup_task (current );
Preempt_enable_no_resched ();
Schedule ();
Preempt_disable ();
/* Call into cpu_idle with preempt disabled */
Cpu_idle ();
}
Let's take a look at kernel_init:
Static int _ init kernel_init (void * unused)
{
/*
* Wait Until kthreadd is all set-up.
*/
Wait_for_completion (& kthreadd_done );
/*
* Init can allocate pages on any node
*/
Set_mems_allowed (node_states [n_high_memory]);
/*
* Init can run on any CPU.
*/
Set_cpus_allowed_ptr (current, cpu_all_mask );
/*
* Tell the world that we're re going to be the grim
* Reaper of innocent orphaned children.
*
* We don't want people to have to make incorrect
* Assumptions about where in the task array this
* Can be found.
*/
Init_pid_ns.child_reaper = current;
Cad_pid = task_pid (current );
Smp_prepare_cpus (setup_max_cpus );
Do_pre_smp_initcils ();
Smp_init ();
Sched_init_smp ();
# Here, this function will unveil the device model initialization.
Do_basic_setup ();
# See the following ~~
/* Open the/dev/console on the rootfs, this shoshould never fail */
If (sys_open (const char _ User *) "/dev/console", o_rdwr, 0) <0)
Printk (kern_warning "Warning: Unable to open an initial console./N ");
(Void) sys_dup (0 );
(Void) sys_dup (0 );
/*
* Check if there is an early userspace init. If yes, let it do all
* The work
*/
If (! Ramdisk_execute_command)
Ramdisk_execute_command = "/init ";
If (sys_access (const char _ User *) ramdisk_execute_command, 0 )! = 0 ){
Ramdisk_execute_command = NULL;
Prepare_namespace ();
}
/*
* OK, we have completed the initial bootup, and
* We're essential up and running. Get rid of
* Initmem segments and start the user-mode stuff ..
*/
Init_post ();
Return 0;
}
Follow do_basic_setup () to go in:
# Well, there are only a few lines to follow.
Static void _ init do_basic_setup (void)
{
Cpuset_init_smp ();
Usermodehelper_init ();
Init_tmpfs ();
# We thought about the long journey over time and finally found our base camp, which is our key analysis goal.
# How can I express my excitement? It is hard to express it, because it is hard to come.
# Don't let us down if we have avoided much complicated code.
# This initialization function creates a very powerful device model system
# Providing continuous stream for devices in PCs
# It supports all devices in this PC
# This is the device model! This is also its charm
Driver_init ();/* build Device Module subsystem */
Init_irq_proc ();
Do_ctors ();
# This initialization function can be called and stored in the kernelInitialization Section.
Do_initcils ();/* init devices */
}
Now let's take a look at this function do_initcils ():
# Here are the external variables
Extern initcall_t _ initcall_start [], _ initcall_end [], _ early_initcall_end [];
Static void _ init do_initcils (void)
{
Initcall_t * fN;
For (fn = _ early_initcall_end; FN <_ initcall_end; FN ++)
Do_one_initcall (* fN );
/* Make sure there is no pending stuff from the initcall sequence */
Flush_scheduled_work ();
}
The following content introduces the initialization section. This name is obtained by myself. It sounds like an image. If we compile the kernel, we will know that there is a link to the script file vmlinux. this file is stored in arch/x86/kernel/vmlinux. lDs. the files written in S assembly language are compiled. The information stored in this file refers to the kernel target file compiled in the entire directory (generally. O suffix) According to vmlinux. make up a complete kernel vmlinux by linking the link information of LDS. That is to say, some custom segments in the kernel code, such as the data segment, BSS segment, and data segment, will be linked according to the link script information.
The initialization section appears here, which should be the address of the function to be called during kernel initialization. In vmlinux. LDS (note that vmlinux. LDS can be obtained only after compilation)
* (. 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 = .;
There are also some macros corresponding to these sections, which can be provided to those modules that need to be called by the kernel during initialization.
From include/Linux/init. h:
# DEFINE _ define_initcall (Level, FN, ID )/
Static initcall_t _ initcall _ # FN # ID _ used/
_ Attribute _ (_ Section _ (". initcall" level ". init") = FN
/*
* Early initcallrun before initializing SMP.
*
* Only for built-in code, not modules.
*/
# Define early_initcall (FN) _ define_initcall ("early", FN, early)
/*
* A "pure" initcall has no dependencies on anything else, and purely
* Initializes variables that couldn't be statically initialized.
*
* This only exists for built-in code, not for modules.
*/
# 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 ("1 s", FN, 1 s)
# Define postcore_initcall (FN) _ define_initcall ("2", FN, 2)
# Define postcore_initcall_sync (FN) _ define_initcall ("2 s", FN, 2 S)
# Define arch_initcall (FN) _ define_initcall ("3", FN, 3)
# Define arch_initcall_sync (FN) _ define_initcall ("3 S", FN, 3 S)
# Define subsys_initcall (FN) _ define_initcall ("4", FN, 4)
# Define subsys_initcall_sync (FN) _ define_initcall ("4 s", FN, 4S)
# Define fs_initcall (FN) _ define_initcall ("5", FN, 5)
# Define fs_initcall_sync (FN) _ define_initcall ("5 s", 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 ("6 s", FN, 6 S)
# Define late_initcall (FN) _ define_initcall ("7", FN, 7)
# Define late_initcall_sync (FN) _ define_initcall ("7 s", FN, 7 S)
See, the above define is defined. Let's talk about it, for example, subsys_initcall (usb_init ).
# Define subsys_initcall (FN) _ define_initcall ("4", FN, 4)
# DEFINE _ define_initcall (Level, FN, ID )/
Static initcall_t _ initcall _ # FN # ID _ used/
_ Attribute _ (_ Section _ (". initcall" level ". init") = FN
You can get ==>
Static initcall_t _ initcallusb4 _ used _ attribute _ (_ Section _ (". initcall4.init") = usb_init. Why does this Code put the USB _ init function for initializing the USB device module in ". in the initcall4.init "initialization section, I am afraid I have to learn some books on GCC compilation and links. Of course, we will not go over these questions. Oh! Yes! Here there is another type that does not say initcall_t. This is a function pointer. I don't know it. It is defined at the top of the file.
Typedef int (* initcall_t) (void );
Typedef void (* exitcall_t) (void );
Now, I have finished speaking the knowledge part. Now, the do_initcils function is not difficult.
# Check the initialization section above to find out where the definition is.
Extern initcall_t _ initcall_start [], _ initcall_end [], _ early_initcall_end [];
Static void _ init do_initcils (void)
{
Initcall_t * fN;
For (fn = _ early_initcall_end; FN <_ initcall_end; FN ++)
Do_one_initcall (* fN); # continue to see how it is done. In fact, we can guess it without looking at it!
/* Make sure there is no pending stuff from the initcall sequence */
Flush_scheduled_work ();
}
View do_one_initcall ():
Int _ init_or_module do_one_initcall (initcall_t FN)
{
Int COUNT = preempt_count ();
Int ret;
If (initcall_debug)
Ret = do_one_initcall_debug (FN );
Else
Ret = FN (); # It must be called like this. We also know it, huh, huh!
# Directly ignore the following
Msgbuf [0] = 0;
If (Ret & RET! =-Enodev & initcall_debug)
Sprintf (msgbuf, "error code % d", RET );
If (preempt_count ()! = Count ){
Strlcat (msgbuf, "preemption imbalance", sizeof (msgbuf ));
Preempt_count () = count;
}
If (irqs_disabled ()){
Strlcat (msgbuf, "disabled interrupts", sizeof (msgbuf ));
Local_irq_enable ();
}
If (msgbuf [0]) {
Printk ("initcall % pf returned with % s/n", FN, msgbuf );
}
Return ret;
}
Well, now everything is ready, just analyze it. From the above content, we know what we are doing and have to sort out our ideas. We found the most important function of analyzing the device model, driver_init (), we also analyzed the function in the initialization section that will be called during kernel initialization. The function in the initialization section is generally a module initialization function, but I have not read it in detail, so I can only talk about it. Modules are a very important feature of Linux kernel. Static compilation and dynamic loading of modules make Linux very flexible, especially for device drivers. If you do not have a conceptual understanding of the kernel module, please read this book, and then you will know how to write the kernel driver. Also, I will talk about it in the general book. Actually, there is not much content, it can be done in a few days.
I was very excited to analyze the device model I thought about right away. However, it took a lot of time to analyze the extensive and profound content. Therefore, I don't know if the subsequent articles will be followed up, but in order to make the device model more thorough, I think it is great to spend more time writing.
We have already completed the entire process from kernel startup to initialization. How much have you learned? To be honest, the articles I wrote must be based on a certain understanding of a lot of knowledge, I have analyzed the code, but I don't know much about it in many places. It works well to read these articles. For many of you who want to learn the kernel source code, but there are still many other conditions that haven't kept up, don't worry about the source code. This will be worth the effort. In the next article, if you want to write, I will write some basic conditions and qualities for learning the kernel. Why are there quality? What do you think!