Analizar la implementación básica del proceso de inicio de Linux.

Descargue la URL del kernel de Linux:

https://www.kernel.org/

El último kernel de Linux es la versión 5.15. Ahora, los códigos fuente del kernel de Linux de uso común son las versiones 4.14, 4.19, 4.9, etc. Entre ellos, el paquete comprimido de código fuente de la versión 4.14 es aproximadamente 90+M, y después de la descompresión, es 700+M, con un total de 61350 archivos. . Con tantos archivos, será difícil verlos con información de origen o VSCode, por lo que puede verlos en línea.

Vea la URL fuente del kernel de Linux en línea:

https://elixir.bootlin.com/linux/latest/source

Vea el código fuente de Android en línea:

http://androidxref.com/

El sistema Android se basa en el kernel de Linux, la capa inferior es el kernel de Linux y la cantidad de código fuente se ha duplicado muchas veces. Por lo tanto, es más difícil utilizar el software para ver el código fuente de Android y puede utilizar el sitio web en línea para ver el código fuente.

Sabemos que hay un gestor de arranque antes del inicio del sistema Linux, como el uboot de uso común, este artículo no analiza el inicio de uboot, solo presenta un diagrama de flujo:

imagen

Este artículo explica principalmente el proceso de inicialización del sistema mediante esta función después de saltar del gestor de arranque a la función de inicio start_kernel del sistema Linux.

En el archivo linux4.14/arch/arm/kernel/head.S, es la inicialización de la etapa de ensamblaje final, y luego saltará a la función start_kernel del archivo main.c, donde se realiza la inicialización del inicio de Linux. , y esta función llamará Se utilizan casi 100 funciones para completar la inicialización del sistema Linux, y las funciones de llamada son las siguientes (diferentes versiones del kernel, el orden y los detalles varían):

linux4.14/init/main.c, función start_kernel.

asmlinkage __visible void __init start_kernel(void)
{
 char *command_line;
 char *after_dashes;

 set_task_stack_end_magic(&init_task);
 smp_setup_processor_id();
 debug_objects_early_init();

 cgroup_init_early();

 local_irq_disable();
 early_boot_irqs_disabled = true;
 /*
  * Interrupts are still disabled. Do necessary setups, then
  * enable them.
  */
 boot_cpu_init();
 page_address_init();
 pr_notice("%s", linux_banner);
 setup_arch(&command_line);
 /*
  * Set up the the initial canary and entropy after arch
  * and after adding latent and command line entropy.
  */
 add_latent_entropy();
 add_device_randomness(command_line, strlen(command_line));
 boot_init_stack_canary();
 mm_init_cpumask(&init_mm);
 setup_command_line(command_line);
 setup_nr_cpu_ids();
 setup_per_cpu_areas();
 smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
 boot_cpu_hotplug_init();

 build_all_zonelists(NULL);
 page_alloc_init();

 pr_notice("Kernel command line: %s\n", boot_command_line);
 /* parameters may set static keys */
 jump_label_init();
 parse_early_param();
 after_dashes = parse_args("Booting kernel",
      static_command_line, __start___param,
      __stop___param - __start___param,
      -1, -1, NULL, &unknown_bootoption);
 if (!IS_ERR_OR_NULL(after_dashes))
  parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
      NULL, set_init_arg);
 /*
  * These use large bootmem allocations and must precede
  * kmem_cache_init()
  */
 setup_log_buf(0);
 pidhash_init();
 vfs_caches_init_early();
 sort_main_extable();
 trap_init();
 mm_init();

 ftrace_init();

 /* trace_printk can be enabled here */
 early_trace_init();
 /*
  * Set up the scheduler prior starting any interrupts (such as the
  * timer interrupt). Full topology setup happens at smp_init()
  * time - but meanwhile we still have a functioning scheduler.
  */
 sched_init();
 /*
  * Disable preemption - early bootup scheduling is extremely
  * fragile until we cpu_idle() for the first time.
  */
 preempt_disable();
 if (WARN(!irqs_disabled(),
   "Interrupts were enabled *very* early, fixing it\n"))
  local_irq_disable();
 radix_tree_init();
 /*
  * Allow workqueue creation and work item queueing/cancelling
  * early.  Work item execution depends on kthreads and starts after
  * workqueue_init().
  */
 workqueue_init_early();

 rcu_init();

 /* Trace events are available after this */
 trace_init();

 context_tracking_init();
 /* init some links before init_ISA_irqs() */
 early_irq_init();
 init_IRQ();
 tick_init();
 rcu_init_nohz();
 init_timers();
 hrtimers_init();
 softirq_init();
 timekeeping_init();
 time_init();
 sched_clock_postinit();
 printk_safe_init();
 perf_event_init();
 profile_init();
 call_function_init();
 WARN(!irqs_disabled(), "Interrupts were enabled early\n");
 early_boot_irqs_disabled = false;
 local_irq_enable();

 kmem_cache_init_late();
 /*
  * HACK ALERT! This is early. We're enabling the console before
  * we've done PCI setups etc, and console_init() must be aware of
  * this. But we do want output early, in case something goes wrong.
  */
 console_init();
 if (panic_later)
  panic("Too many boot %s vars at `%s'", 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();
 /*
  * This needs to be called before any devices perform DMA
  * operations that might use the SWIOTLB bounce buffers. It will
  * mark the bounce buffers as decrypted so that their usage will
  * not cause "plain-text" data to be decrypted when accessed.
  */
 mem_encrypt_init();

#ifdef CONFIG_BLK_DEV_INITRD
 if (initrd_start && !initrd_below_start_ok &&
     page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
  pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
      page_to_pfn(virt_to_page((void *)initrd_start)),
      min_low_pfn);
  initrd_start = 0;
 }
#endif
 kmemleak_init();
 debug_objects_mem_init();
 setup_per_cpu_pageset();
 numa_policy_init();
 if (late_time_init)
  late_time_init();
 calibrate_delay();
 pidmap_init();
 anon_vma_init();
 acpi_early_init();
#ifdef CONFIG_X86
 if (efi_enabled(EFI_RUNTIME_SERVICES))
  efi_enter_virtual_mode();
#endif
 thread_stack_cache_init();
 cred_init();
 fork_init();
 proc_caches_init();
 buffer_init();
 key_init();
 security_init();
 dbg_late_init();
 vfs_caches_init();
 pagecache_init();
 signals_init();
 proc_root_init();
 nsfs_init();
 cpuset_init();
 cgroup_init();
 taskstats_init_early();
 delayacct_init();

 check_bugs();

 acpi_subsystem_init();
 arch_post_acpi_subsys_init();
 sfi_init_late();

 if (efi_enabled(EFI_RUNTIME_SERVICES)) {
  efi_free_boot_services();
 }
 /* Do the rest non-__init'ed, we're now alive */
 rest_init();

 prevent_tail_call_optimization();
}

Entre ellas, siete funciones son las más importantes, a saber:

setup_arch(&command_line);

mm_init();

sched_init();

init_IRQ();

consola_init();

vfs_caches_init();

rest_init();

1、setup_arch(&command_line)

Esta función es la función de inicialización de la arquitectura del sistema, que maneja los parámetros pasados ​​​​por uboot. Diferentes arquitecturas realizan diferentes inicializaciones, es decir, cada arquitectura tendrá una función setup_arch.

linux4.14/arch/arm/kernel/setup.c

imagen

2、mm_init

función de inicialización de memoria

linux4.14/init/main.c

imagen

3、sched_init

Se inicializa el programador de procesos del kernel. El kernel de Linux implementa cuatro métodos de programación, generalmente utilizando el método de programación CFS. Como sistema operativo universal, se deben considerar varios requisitos: no podemos solo especificar el tiempo de ejecución del proceso de acuerdo con la prioridad de interrupción o el intervalo de rotación de tiempo. Como sistema operativo multiusuario, se debe considerar la equidad de cada usuario. No es posible limitar el tiempo de ejecución del proceso de un usuario solo porque no tiene privilegios avanzados, es necesario considerar que cada usuario tiene una cantidad justa de tiempo.

linux4.14/kernel/sched/core.c

imagen

4、init_IRQ

Función de inicialización de interrupciones, esto se entiende bien, todos han usado interrupciones.

linux4.14/arch/arm/kernel/irq.c

imagen

5、console_init

Antes de que se inicialice esta función, todas las funciones de impresión del kernel printk que escriba no imprimirán nada. Antes de que se inicialice esta función, todas las impresiones se almacenarán en buf. Después de que se inicialice esta función, los datos en buf se imprimirán, para que pueda ver qué imprime printk en el terminal.

tty es una terminal en Linux, y las dos oraciones _con_initcall_start y _con_initcall_end significan ejecutar todas las funciones initcall entre ellas.

linux4.14/kernel/printk/printk.c

imagen

6、vfs_caches_init

En este paso se monta la inicialización del sistema de archivos virtual, como sysfs, sistema de archivos raíz, etc.. proc es un kernel virtual, que se utiliza para generar información de la estructura de datos del kernel, que no se cuenta aquí.

El sistema de archivos virtual vfs protege la diferencia del hardware subyacente, proporciona una interfaz unificada y facilita el trasplante y el uso del sistema. Permite a los usuarios transferir códigos directamente a otras plataformas sin cambiar el código de la aplicación.

linux4.14/fs/dcache.c

imagen

El montaje aquí se realiza principalmente en la función mnt_init():

linux4.14/fs/namespace.c

imagen

7、rest_init

Esta función puede considerarse como la última función llamada por la función start_kernel. Aquí se generan los dos procesos del kernel más importantes, kernel_init y kthreadd. Después de kernel_init, saltará del espacio del kernel al espacio del usuario y se convertirá en el proceso de inicio del El espacio de usuario, PID=1 y kthreadd, PID=2, es un proceso del kernel, que se usa especialmente para monitorear la solicitud para crear un proceso del kernel. Mantiene una lista vinculada. Si es necesario crear un proceso del kernel, se creará en la lista vinculada.

En este punto, ha salido el proceso de inicio más importante en el espacio del usuario, y todos los procesos posteriores del espacio del usuario son bifurcados por el proceso de inicio. Si es un sistema Android, el proceso de inicio bifurcará un proceso cigoto, que es el proceso principal de todos los procesos del sistema Android.

Linux4.14/init.main.c

imagen

imagen

En la figura anterior, el proceso kernel_init se crea en la línea 400 y el proceso kthreadd se crea en la línea 412, los cuales son procesos del kernel. La línea 426 notifica al proceso kernel_init que se ha creado kthreadd. Es decir, kthreadd en realidad se ejecuta primero y kernel_init se ejecuta en segundo lugar.

Puede consultar los siguientes artículos para comprender el resto de funciones:

https://www.cnblogs.com/andyfly/p/9410441.html

https://www.cnblogs.com/lifexy/p/7366782.html

https://www.cnblogs.com/yanzs/p/13910344.html#radix_tree:init

Supongo que te gusta

Origin blog.csdn.net/weixin_41114301/article/details/132221142
Recomendado
Clasificación