2. Análisis del proceso de inicio del kernel de Linux

El último blog explicó la estructura del código y el proceso de compilación del kernel de Linux . Este blog explica principalmente el proceso de inicio del núcleo, que es útil para portar el núcleo. Para comprender el proceso de inicio del kernel, necesita conocer el propósito del kernel. El propósito principal de algunas operaciones complicadas y engorrosas antes de que se inicie el kernel es dejar que el kernel se ejecute. ¿Cuál es el propósito de la operación del kernel? Debe ser para ejecutar aplicaciones, por lo que el propósito principal del núcleo es montar el sistema de archivos raíz y luego ejecutar sus propios programas . Dado que el kernel de Linux admite una variedad de marcos de CPU y una variedad de placas individuales como u-boot , existen funciones de inicialización relacionadas con la arquitectura y las placas de desarrollo . El proceso de inicio del núcleo también se puede dividir en dos partes: el proceso de inicio relacionado con la placa de arquitectura / desarrollo y el proceso de inicio general posterior. La siguiente figura es el proceso de inicio del kernel de Linux vmlinux en el procesador de arquitectura ARM. El kernel vmlinux se usa aquí porque el kernel de otros formatos (Image, uImage, etc.) obtendrá vmlinux después de algunas operaciones especiales, y luego iniciará vmlinux.

La primera etapa generalmente usa programación en lenguaje ensamblador, ya que el kernel admite múltiples arquitecturas de CPU y varias placas de desarrollo, es necesario verificar si el kernel es compatible con dichas arquitecturas de CPU y placas de desarrollo . Al final del arranque de BootLoader, el código de la máquina se pasará al kernel. Después de que se pasa la prueba, debido a que la dirección virtual se usa al vincular el kernel, debe configurar la tabla de páginas y habilitar el MMU- > prepararse para llamar al entorno C (start_kernel) de la siguiente etapa, por lo que debe copiar el segmento de datos, borrar el segmento BBS, configurar la pila , Llame a la función start_kernel (es decir, la dirección virtual utilizada por el núcleo) .

El flujo de código de la primera etapa se analiza a continuación (arch / arm / kernel / head.S)

Lo anterior es el contenido en arch / $ (ARCH) /head.S, que se utiliza principalmente para detectar si el núcleo admite la arquitectura de la CPU y la placa única (la placa única es el primer parámetro que pasa BootLoader cuando se inicia el núcleo (guardar en R1 )), Crear una tabla de página de primer nivel, habilitar MMU. La siguiente es una explicación detallada.

①Compruebe si el núcleo es compatible con la CPU de esta placa de desarrollo. El ID de la CPU se puede obtener de C0 en el coprocesador CP15:

Número de fabricante (8 bits) ARM utiliza 0x41 Sub número del producto (4 bits) Número de versión del sistema ARM (4 bits) Número maestro de producto (8 bits) Número de versión del procesador (4 bits)

La función anterior se define en head_common.S. Para la primera línea en la función __lookup_processor_type es obtener la dirección física de la etiqueta 3: en R3 (la función MMU no está habilitada en este momento, por lo que la CPU usa la dirección física) Nota: La dirección obtenida por la instrucción ADR se basa en el registro de la PC calculada . La segunda línea de código es guardar los valores de __proc_info_begin __proc_info_end y. (Estas son las direcciones utilizadas en el script de enlace, que son direcciones virtuales) en r5-r7. Esto es para calcular la desviación entre la dirección física y la dirección virtual, y las dos últimas son para calcular las direcciones físicas de __proc_info_begin y __proc_info_end. Etiqueta 1: Lo que se hace dentro es leer la información de proc_info_list de __proc_info_begin a __proc_info_end (el prototipo de la estructura proc_info_list se define en include / asm-arm / procinfo.h, que significa la CPU soportada por el núcleo. Para la arquitectura ARM CPU estas estructuras El cuerpo se define en el directorio arch / arm / mm , como proc_arm920.S, que representa el archivo de definición de la estructura y otra información de proc_info_list de la CPU de arquitectura arm920. Se utilizan diferentes estructuras de proc_info_list para admitir CPU en diferentes instituciones, todas están definidas En la sección .proc_info_init. Estas estructuras se organizan juntas (__proc_info_begin es la dirección de inicio ), y luego se comparan con el valor CPUID (r9) leído desde el procesador para ver si pertenece a la misma CPU. Si coincide, se transfiere a la etiqueta 2: Regrese a la ubicación, si no coincide, continúe leyendo y luego coincida. Ninguna coincidencia fue exitosa hasta el final, y r5 se asignó a 0 para finalizar.nota: la CPU de diferentes placas de desarrollo puede ser diferente. La CPU de la arquitectura ARM se define en el directorio arch / arm / mm /, que define la información relevante de la CPU, como la __arm920_proc_info (estructura proc_info_list) definida en el archivo proc_arm920.S. Entonces, cuando use esta CPU, debe incluir este archivo. Al configurar el kernel, debe configurarlo (el menú de configuración es Tipo de sistema-> Soporte del procesador ARM920T). Es la misma razón para usar otros chips .

②Compruebe si el núcleo admite la ID de máquina de esta placa de desarrollo. La ID de máquina se pasa cuando BootLoader inicia el kernel (almacenado en R1). La función __look_machine_type también es similar a __look_processor_type y también se define en head_common.S .

Para cada placa de desarrollo compatible en el kernel, se define una estructura machine_desc, que define algunas propiedades y funciones relacionadas con la placa de desarrollo, como el ID del tipo de máquina, la dirección física de E / S de inicio y la función de inicialización de interrupción . Para el archivo de tabla de definición de la arquitectura ARM almacenada en el arco / brazo / mach-xxx / xxxx.c en , que contiene alguna información relacionada con la tarjeta de desarrollo, machine_desc prototipo se define en el cuerpo include / asm-brazo / mach / En arch.h , todas las estructuras machine_desc están en la sección .arch.info.init. La dirección inicial de esta sección es __arch_info_begin, y la dirección final es __arch_info_end. Para usar las placas de desarrollo relevantes, también debe configurar el kernel. También debe incluir los archivos relevantes en el kernel ( como arch / arm / math-s3c2410 / mach-smdk2410.c ). El menú de configuración está en tipo de sistema-> Máquina XXX- > xx En.

        La función __lookup_machine_type es similar a la función __lookup_processor_type anterior. La dirección de inicio virtual y la dirección final de la sección .arch_info_init se obtienen y se convierten en direcciones físicas. Luego lea la ID de la máquina en machine_desc de la dirección de inicio del segmento y compárela con R1. Si coincide, salte a la etiqueta 2 y regrese. Si no coinciden, continúe leyendo la información en el interior. Si no coinciden, la tarea R5 = 0 finaliza.

③ Cree una tabla de páginas para habilitar la MMU, inicialice la pila, borre el segmento BSS y copie el segmento de datos, etc., y llame a start_kernel (en head-common.S) para ingresar a la segunda etapa del kernel.

Después de llamar a __enable_mmu para habilitar la función MMU, la función finalmente ejecuta las cosas en R13 (es decir, el __switch_data guardado anteriormente). __Switch_data se define en head_common.S, que define muchas funciones.

Al final de la función anterior, llame a start_kernel en main.c para ingresar a la segunda etapa.

La segunda etapa del inicio del kernel:

Hay dos tipos de parámetros que u-boot pasa al kernel: uno es la lista de etiquetas de una dirección que existe de antemano, y el otro es la ID de máquina especificada en el registro R1 cuando se llama al kernel. La ID de la máquina se usará durante la fase de arranque (cuando se detecte la ID de la máquina en lo anterior), la lista de etiquetas se procesará inicialmente en la función setup_arch.

La función de la función set_arch: hacer algunas configuraciones relacionadas con el procesador-> hacer algunas configuraciones relacionadas con la placa de desarrollo-> lista de etiquetas de proceso-> parámetros de línea de comando de proceso-> reinicializar tabla de páginas paging_init (& meminfo, mdesc)

En la tabla de la página de inicialización paging_init, el segundo parámetro es la estructura machine_desc devuelta por la función lookup_machine_type anterior. Esta estructura se define en la función arch / arm / mach-xxx / mach-smdkxxx.c. La definición de la placa de desarrollo s3c2440 es la siguiente. Esta estructura es a menudo importante, la inicialización a nivel de placa se define aquí

Map_io en paging_init-> devicemap_init-> mdesc-> map_io () es el miembro del puntero de función .map_io en esta estructura . Necesita modificar el siguiente reloj, modifique 16934400 a 12MHz.

Función de inicialización de la consola:

Llame a cada función definida entre __con_initcall_start y __con_initcall_end. Estas funciones se especifican utilizando la macro console_initcall (). Esta macro se utiliza para definir una función con el atributo de .con_initcall_init.

Después de una serie de inicializaciones, start_kernel llamará rest_init (); en la función rest_init (), se creará el hilo kernel kernel_init. Principalmente para montar el sistema de archivos y ejecutar la aplicación, monte el sistema de archivos raíz en prepare_namespace () .

Después de montar el sistema de archivos, ejecute init_post (), que abre principalmente el archivo del dispositivo / dev / console y luego llama a run_init_process () para ejecutar el programa init.

El kernel se ha ejecutado para iniciar el primer proceso init, init bifurcará varias aplicaciones. Entonces puede comenzar a ejecutar contenido en el espacio de usuario.

Publicado 35 artículos originales · Me gusta1 · Visitas 1870

Supongo que te gusta

Origin blog.csdn.net/lzj_linux188/article/details/102695522
Recomendado
Clasificación