"Somos flojos y curiosos"

Esta vez, la razón de la publicación fue un artículo en una buena revista dedicada al sistema operativo Linux (en lo sucesivo, L), en la que el "experto" atraído elogió al controlador que conectaba la pantalla LCD a la placa Raspbery. Dado que tales cosas (conexión, no el sistema operativo) están dentro del alcance de mis intereses profesionales, revisé el artículo con atención, luego encontré el texto real del "controlador" y me sorprendió un poco que pudiera elogiarse. Bueno, en general, el nivel de experto puede determinarse solo porque obstinadamente llamó al programa conductor, a pesar de que no lo es. Parecería, e higo con él, nunca se sabe lo que alguien escribe para sí mismo, pero publicar esto en el dominio público: "No sabía que era posible".
El hecho de que la dirección del dispositivo en el bus I2C se estableciera directamente en el texto del programa y para su cambio requirió su recompilación (es bueno que no todo el núcleo) esté especialmente satisfecho. Por cierto, noté que en los foros dedicados a L, la respuesta más popular a cualquier pregunta sobre problemas de software es "reconstruir la última versión del kernel". Este enfoque me parece un poco extraño, probablemente, no sé algo. Pero, sin embargo, surgió la pregunta de cómo se implementa realmente la parametrización del controlador (dentro, no fuera: todo es simple y claro) en A, la respuesta a la que se dedica esta publicación.
No es que escribiera constantemente controladores para L, pero con el proceso en su conjunto estoy familiarizado y Google confirmó vagos recuerdos de que hay un conjunto de macros que deberían usarse al crear el texto fuente del módulo para poder pasarle parámetros de operación, por ejemplo, la dirección del dispositivo a al bus Sin embargo, la mecánica del proceso en sí no se describió en ninguna parte. Vi el mismo texto en numerosos enlaces (por cierto, una pregunta interesante: ¿por qué hacer esto, es decir, colocar el texto de otra persona en mi recurso? No entiendo realmente el significado de esta operación), que describe las macros anteriores. No encontré una sola mención del mecanismo para realizar la operación, para otro sistema operativo conocido (Windows) tendría que decir un hecho y limitarme a esto, pero una de las ventajas de A es la disponibilidad de textos fuente y la capacidad de encontrar una respuesta a cualquier pregunta sobre su estructura interna, lo que haremos Noto de inmediato que trataré de no duplicar la información que puede obtener de otras fuentes, y me limitaré solo a lo que sea necesario para comprender el texto.
Pero, antes de mirar la fuente, primero pensaremos un poco, pero ¿cómo lo haríamos si tuviéramos una tarea similar (y de repente, después de esta publicación, me invitarán a los mineros L, y no te negarás). Por lo tanto, existe la posibilidad de crear un módulo, una cierta unidad de programa especialmente diseñada que se puede cargar en la memoria para su ejecución utilizando alguna utilidad del sistema (insmode, en adelante I), mientras se pasa una cadena de caracteres como parámetros de inicio. Esta línea puede incluir unidades léxicas estrictamente definidas, cuya descripción del formato se especifica al crear el texto fuente del módulo, y estas unidades contienen información que le permite cambiar el valor de las variables internas de este módulo.
Consideremos más cuidadosamente la forma de describir las unidades léxicas anteriores, necesitamos esto para considerar varias soluciones. La unidad de análisis se determina llamando a la macro, que se informa de la información necesaria: el nombre de la variable que se modificará durante el proceso de configuración, su nombre externo (generalmente el mismo que el anterior), el tipo de variable del conjunto limitado y los derechos de acceso a la variable en el estilo de rw-rw-rw. Además, se puede especificar una cadena de texto (opcional) que describe la variable. Obviamente, esta información es necesaria y suficiente (junto con las reglas para el diseño de unidades sintácticas - separadores y tokens) para construir el analizador de la lista de parámetros especificada en forma de una cadena de texto, pero deja espacio para la implementación de la distribución de funciones entre el participante del proceso.
Para configurar el módulo necesitamos:
- formulario (bueno, esto está en la etapa de compilación, puedes hacerlo como quieras, aunque todavía es interesante cómo) y almacenar una tabla de las configuraciones anteriores,
- analizar parámetros de entrada de acuerdo con esta tabla y
- realizar cambios en ciertas áreas de la memoria de acuerdo con el resultado de analizar una unidad sintáctica.
Pensaremos un poco en el estilo de "si yo fuera el director" y propondremos posibles implementaciones. Cómo podríamos implementar el comportamiento similar de la utilidad del sistema y el módulo: comenzaremos el análisis de las opciones en una complejidad creciente.
La primera solución es la utilidad Y no hace casi nada, solo llama al módulo que se le indica y le transfiere los parámetros restantes en el estilo de línea de comando, y el módulo ya los analiza, basándose en la información disponible en él y realiza las modificaciones necesarias. Esta solución es simple, comprensible y bastante factible, pero se debe tener en cuenta la siguiente circunstancia: de ninguna manera se debe dejar el análisis de parámetros a la voluntad del autor del módulo, ya que esto le proporcionará un espacio inaceptable y, después de todo, dos programadores siempre escribirán tres opciones de analizador. Y entonces fuimos a conocerlo, permitiendo que parámetros de un tipo indefinido, que tienen una cadena de texto como valor, sean suficientes para él.
Por lo tanto, un cierto analizador estándar debe incluirse automáticamente en el texto del módulo, esto es fácil de implementar en el nivel de macro sustitución.
Esta solución tiene dos inconvenientes:
- no está claro por qué lo necesitamos en absoluto, y puede llamar al módulo de inmediato con los parámetros desde la línea de comandos
- el código del módulo (parte de inicialización) debe contener las tres secciones de la información necesaria, y esta información es necesaria solo cuando se inicia el módulo y no se utiliza en el futuro, y siempre ocupa espacio. Inmediatamente haga una reserva de que esta información necesariamente ocupa espacio en el archivo, pero puede no ir a la memoria al cargar el módulo, si todo se hace con cuidado. Para hacer exactamente eso, recordamos las directivas _init e _initdata (por cierto, pero cómo funcionan, tendríamos que resolverlo, este es el tema de la próxima publicación, ¿lo esperarán?). Pero en el último caso, las secciones 2 y 3 de la información en el archivo son claramente redundantes, ya que el mismo código estará presente en muchos módulos, violando maliciosamente el principio DRY.
Debido a las deficiencias señaladas, la implementación de esta opción es altamente improbable. Además, no está claro por qué en la macro establecer información sobre el tipo de parámetro, porque el módulo en sí mismo sabe muy bien lo que modifica (aunque puede ser necesario para el analizador cuando verifica los parámetros). La evaluación general de la probabilidad de tal decisión es del 2-3 por ciento.
La digresión necesaria sobre el inconveniente número 2: me formé como especialista en aquellos días en que 256 kbytes de RAM eran suficientes para organizar 4 estaciones de trabajo, 56 kbytes tenían un sistema operativo de doble tarea y un sistema operativo de una sola tarea comenzó a funcionar a 16 kbytes. Bueno, 650 kb, que deberían ser suficientes para cualquier programa, generalmente eran algo del campo de la ficción no científica. Por lo tanto, estoy acostumbrado a considerar la RAM como un recurso escaso y desapruebo su uso innecesario si no es causado por una emergencia (por regla general, requisitos de rendimiento), y en este caso no observo esa situación. Como la mayoría de mis lectores se han formado en diferentes realidades, es posible que tenga sus propias evaluaciones de la preferencia de esta o aquella opción.
La segunda solución: el analizador en sí mismo se transfiere a AND, que transfiere los datos extraídos al módulo (su parte de inicialización): número y valor del parámetro. Luego preservamos la uniformidad de los parámetros y reducimos los requisitos para el tamaño del módulo. La pregunta sigue siendo cómo proporcionar una lista AND de posibles parámetros, pero esto es proporcionado por las macros creando una estructura predeterminada del módulo y la ubicación del bloque en algún lugar específico (archivo o memoria). La solución es mejor que la anterior, pero aún queda el exceso de memoria en el módulo. En general, me gusta la solución, porque mi analizador (que es peor que todos los demás programadores, tengo mi propio analizador, no sin fallas, pero definitivamente no fatal) funciona de acuerdo con este esquema, devolviendo el número de la regla y el valor identificados al programa principal parámetro. Sin embargo, la probabilidad de implementar esta opción en particular no es muy alta: 5 por ciento.
Una subopción de la segunda solución es transferir los parámetros extraídos no a la parte de inicio del módulo, sino directamente a su parte de trabajo cargada, por ejemplo, a través de ioctl: los requisitos de memoria son los mismos. Tenemos una oportunidad única para cambiar los parámetros "sobre la marcha", que no se implementa en otras versiones. No está muy claro por qué podríamos necesitar dicha función, pero se ve hermosa. La desventaja es que 1) necesita reservar una parte del área de funciones con anticipación para una solicitud posiblemente no utilizada, y 2) el código del modificador debe estar presente en la memoria constantemente. Evaluación de la probabilidad de implementación - porcentaje 5.
La tercera solución es transferir a Y también la modificación de los parámetros. Luego, en el proceso de cargar el código binario del módulo Y, puede modificar los datos en la memoria intermedia y cargar el código del controlador con los parámetros modificados en el lugar de implementación permanente, o realizar estas modificaciones directamente en el área de memoria en la que se cargó el binario, y la tabla de parámetros presente en el archivo está en la memoria puede cargarlo y no ocuparlo (recuerde las directivas). La decisión es responsable, requerirá, como la anterior, la presencia de un área de comunicación predefinida entre el módulo y AND para almacenar la descripción de los parámetros, pero reduce aún más los requisitos de memoria excesiva en el módulo. Inmediatamente, notamos el principal inconveniente de tal solución: la incapacidad de controlar los valores de los parámetros y su consistencia, pero no hay nada que hacer. Es una solución bastante normal, lo más probable es que sea un 75 por ciento.
Una variante de la tercera solución: la información sobre los parámetros no se almacena en el módulo en sí, sino en algún archivo auxiliar, entonces simplemente no hay exceso de memoria en el módulo. En principio, lo mismo se puede hacer en la versión anterior, cuando el módulo contiene la parte de configuración, que AND utiliza durante el proceso de arranque, pero no se carga en la RAM que contiene la parte ejecutable real del módulo. En comparación con la versión anterior, se agregó un archivo adicional y no está claro lo que estamos pagando, pero tal vez hicieron directivas de inicialización antes de la invención: 5 por ciento.
El 7 por ciento restante se destinará a otras opciones que no se me ocurrieron. Bueno, ahora que nuestra fantasía se ha agotado (la mía es segura, si hay más ideas, por favor pregunte en el comentario), comenzaremos a estudiar la fuente de L.
Para empezar, observo que, aparentemente, el arte de distribuir textos fuente en archivos se perdió junto con el sistema operativo, que cabe en 16 kb, ya que la estructura del directorio, sus nombres y nombres de archivos están conectados con el contenido un poco más que nada. Dada la presencia de inclusiones incrustadas, el estudio clásico de las fuentes descargadas con la ayuda de un editor se convierte en una búsqueda extraña y será improductivo. Afortunadamente, existe una encantadora utilidad Elixir, disponible en línea, que le permite realizar búsquedas contextuales, y aquí con ella el proceso se vuelve mucho más interesante y fructífero. Realicé mi investigación adicional en el sitio elixir.bootlin.com. Sí, este sitio no es una colección oficial de quesos del núcleo, a diferencia de kernel.org, pero esperemos que el código fuente para ellos sea idéntico.
Primero, veamos una macro para determinar los parámetros: en primer lugar, conocemos su nombre y, en segundo lugar, debería ser más fácil (sí, ahora). Se encuentra en el archivo moduleparam.h, es bastante razonable, pero es una sorpresa agradable, dado lo que veremos más adelante. Macro
{0}module_param(name,type,perm)
es una envoltura
{0a}module_param_named(n,n,t,p)
- Azúcar sintáctico para el caso más común. Al mismo tiempo, por alguna razón, la enumeración de los valores permitidos de uno de los parámetros, a saber, el tipo de la variable, se proporciona en los comentarios antes del texto de contenedor, y no en la segunda macro, que realmente hace el trabajo y puede usarse directamente.
La macro {0a} contiene una llamada a tres macros
{1}param_check_##t(n,&v)
(hay un conjunto de macros para todos los tipos válidos)
{2}module_param_cb(n,&op##t,&v,p)
y
{3}__MODULE_PARM_TYPE(n,t)
(preste atención a los nombres, sin embargo, encanto), y el primero de ellos se usa en otros lugares, es decir, las recomendaciones de Occam y el principio KISS también son descuidadas por los creadores de A, aparentemente, algún tipo de base para el futuro. Por supuesto, estas son solo macros, pero no cuestan nada, pero aún así ...
La primera de las tres macros {1}, como su nombre lo indica, verifica la correspondencia de los tipos de parámetros y las envolturas
__param_check(n,p,t)
Tenga en cuenta que en la primera etapa de ajuste, el nivel de abstracción macro disminuye, y en la segunda, probablemente aumenta de una manera diferente, y solo me parece que podría ser más simple y más lógico, especialmente teniendo en cuenta que la macro promedio no se usa en ningún otro lugar. Bien, pongamos otra forma de verificar los parámetros macro en la alcancía y seguir adelante.
Pero las siguientes dos macros en realidad generan un elemento de la tabla de parámetros. ¿Por qué no me preguntas dos y no uno? He dejado de entender la lógica de los creadores de L. Es muy probable que, basado en la diferencia en el estilo de estas dos macros, comenzando con los nombres, el segundo de ellos se agregó más tarde para expandir la funcionalidad y modificar la estructura existente. Era imposible, porque inicialmente se arrepintieron de asignar un lugar para indicar una variante de los parámetros. La macro {2}, como siempre, nos oculta la macro
{2a}_module_param_call(MODULE_PARAM_PREFIX,n,ops,arg,p,-1,0)
(es curioso que esta macro no se llame directamente a ninguna parte, excepto 8250_core.c, donde se llama con los mismos parámetros adicionales), pero esta última ya produce el código fuente.
Una pequeña observación: durante la búsqueda nos aseguramos de que la navegación de texto funcione bien, pero hay dos circunstancias desagradables: la búsqueda por el fragmento de nombre no funciona (no se encontró check_param_, aunque se encontró check_param_byte) y la búsqueda solo funciona en declaraciones de objetos (la variable no se encuentra, entonces se encuentra en este archivo por ctrF, pero no se detecta la búsqueda integrada por fuente). No es muy alentador, porque es posible que necesitemos buscar un objeto fuera del archivo actual, pero "al final, no tenemos otro".
Como resultado del trabajo de {1} en el texto del módulo compilado en presencia de las siguientes dos líneas
module_param_named(name, c, byte, 0x444); module_param_named(name1, i, int, 0x444);
aparece un fragmento del tipo a continuación
static const char __param_str_name[] = "MODULE" "." "name"; static struct kernel_param const __param_name \ __attribute__((__used__)) \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ = { __param_str_name, ((struct module *)0), ¶m_ops_byte, (0x444), -1, 0, { &c } }; static const char __UNIQUE_ID_nametype72[] \ __attribute__((__used__)) __attribute__((section(".modinfo"), unused, aligned(1))) \ = "parmtype" "=" "name" ":" "byte"; static const char __param_str_name1[] = "MODULE" "." "name1"; static struct kernel_param const __param_name1 \ __attribute__((__used__)) \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ = { __param_str_name1, ((struct module *)0), ¶m_ops_int, (0x444), -1, 0, { &i } }; static const char __UNIQUE_ID_name1type73[] __attribute__((__used__)) \ __attribute__((section(".modinfo"), unused, aligned(1))) \ = "parmtype" "=" "name1" ":" "int";
(de hecho, los archivos de una sola línea se generan allí, los dividí en líneas para facilitar su revisión) e inmediatamente podemos decir que no hay indicios de la inclusión de una sección de programa analizador o un módulo para asignar valores a los parámetros en el texto fuente, por lo que las opciones 1 y 2 pueden ser considerado excluido de mayor consideración. La presencia de atributos especiales para el enlazador, por así decirlo, insinúa la existencia de una región de comunicación ubicada en un lugar predeterminado a través del cual se transmite la descripción de los parámetros. Al mismo tiempo, observamos con desconcierto la ausencia total de cualquier descripción del bloque generado de posibles parámetros en forma de texto que podría ser utilizado por el módulo analizador. Está claro que el código bien escrito es autodocumentado, pero no en la misma medida que nuevamente no aumenta la probabilidad de la opción 1 o 2, con el analizador escrito por el desarrollador del módulo.
La combinación de los atributos __used__ y no utilizados al mismo tiempo parece divertida en la última línea generada, especialmente si observa el siguiente fragmento de código macro
#if GCC_VERSION < 30300 # define __used __attribute__((__unused__)) #else # define __used __attribute__((__used__)) #endif
¿Cuál es el tipo de agilidad que los desarrolladores de A fuman, la forma dolorosamente tortuosa de sus pensamientos incorporados en el código? Sé que puede usar ambas formas de escritura de atributos, pero ¿por qué hacer esto en la misma línea? No lo entiendo.
Se puede observar una característica más interesante del código resultante: la duplicación de información sobre el nombre de la variable y su tipo. Todavía no está claro por qué se hizo esto, pero el hecho en sí mismo no está en duda. Por supuesto, esta información es coherente, ya que está construida en modo automático, y esta coherencia se conservará cuando cambie el texto fuente (y esto es bueno), pero está duplicada (y esto es malo), tal vez más adelante comprendamos la necesidad de tal solución. Además, la necesidad de formar un nombre único utilizando el número de línea del código fuente sigue sin estar clara, porque la primera línea generada no lo tenía.
Otra nota: descubrir exactamente en qué se convierte la definición del parámetro no fue una tarea completamente trivial, pero gracias a MinGW, aún se completó. Debajo del capó había una stringificación y doble pegado de parámetros, la formación de nombres únicos, así como otros trucos difíciles de trabajar con macros, pero solo presento los resultados. Resumiendo el resultado intermedio, puedo decir que estudiar macros A no es de lo que me gustaría ganarme la vida, solo es posible como entretenimiento, pero continuamos.
No avanzaremos en la comprensión de las macros para comprender la tarea, por lo que recurriremos al código fuente de la utilidad And e intentaremos comprender lo que hace.
En primer lugar, nos sorprende ver que los quesos requeridos no están incluidos en las fuentes del grano. Sí, estoy listo para aceptar que I es una utilidad e interactúa con el núcleo a través del punto de entrada para cargar el módulo, pero cualquier libro sobre controladores L nos informa sobre esta utilidad, por lo que la falta de una versión "oficial" de su fuente en algún lugar cerca de la fuente del núcleo causa malentendido Bueno, está bien, Google no nos decepcionó, y de todos modos salimos con el queso.
La segunda cosa sorprendente es que esta utilidad se forma a partir de un paquete cuyo nombre no está asociado de ninguna manera con su nombre, hay más de un paquete de este tipo y cada uno se nombra a su manera en diferentes lugares, divertido, por decir lo menos. Si tiene L instalado, entonces con el comando: puede averiguar desde qué paquete está construido el programa de utilidad I y luego buscarlo, pero si llevamos a cabo una investigación teórica (personalmente no mantengo L en la computadora de mi casa por varias razones, algunas de las cuales Dije mis publicaciones, un boxeador teórico), entonces este método no está disponible para nosotros y todo lo que queda es una búsqueda en Internet, afortunadamente, da resultados.
Bueno, la tercera cosa sorprendente es que el nombre de la utilidad en sí no aparece en ninguna parte del código fuente, no se usa en los nombres de archivo y solo se encuentra en el archivo de creación, sé que en C estamos obligados a nombrar la función principal principal, y esto no se discute (personalmente, no estoy en Estoy encantado con esto, ya que Pascal está malcriado, pero no me pidieron mi opinión al diseñar el lenguaje), pero al menos sería posible escribir el nombre externo de la utilidad en los comentarios. Nota necesaria: muchas cosas en el lenguaje C se hicieron según el principio "es tan habitual con nosotros", que probablemente fue difícil hacer las cosas diferentes a veces, o incluso imposibles, pero ¿qué se puede hacer ahora, arrastrando una maleta sin un asa más?
Encontramos dos paquetes que contienen el texto fuente, y también encontramos los quesos en github, vemos que son idénticos y creemos que así es como se ve el código fuente de la utilidad. A continuación, estudiamos solo el archivo en git, especialmente dado que aquí solo se llama insmod.c, encontramos que Y para empezar, convierte la lista de parámetros en una cadena larga con terminación nula, en la que los elementos individuales están separados por espacios. Después de esto, llama a dos funciones, la primera de las cuales se llama grub_file y obviamente abre el binario, mientras que la segunda tiene el nombre init_module y toma un puntero a un archivo abierto con el módulo binario y una cadena de parámetros y se llama load_module, que sugiere el propósito de esta función como cargar con modificación de parámetros.
Pasamos al texto de la segunda función, que se encuentra en el archivo ... y aquí hay un fastidio, no en ninguno de los archivos del repositorio estudiado en Geet (bueno, esto es lógico, esto es parte del núcleo y su lugar no está aquí) no lo es. Google nuevamente tiene prisa por ayudar y nos devuelve a los quesos del núcleo bajo Elixir y el archivo module.c. Cabe señalar que, sorprendentemente, el nombre del archivo que contiene las funciones para trabajar con módulos parece lógico, ni siquiera entiendo cómo explicar esto, probablemente sucedió por accidente.
Ahora nos quedó claro la falta de texto. Y al lado del núcleo: en realidad no hace casi nada, solo transfiere parámetros de un formulario a otro y transfiere el control al núcleo mismo, por lo que incluso es indigno de mentir al lado. A partir de este momento, queda claro que no hay información externa clara sobre la estructura de los parámetros, ya que el núcleo los omitió a través de sus propias macros y sabe todo sobre ellos perfectamente, y el resto no necesita saber nada sobre la estructura interna (a la luz del hecho de que la fuente están disponibles para su visualización, algunos comentarios no afectarían, pero en principio es más y más claro incluso sin ellos), pero hasta ahora casi no ha arrojado luz sobre la implementación del mecanismo de ejecución en sí.
Nota: con respecto a la transferencia de control al kernel, me emocioné un poco, vemos con certeza el uso de la función en el código fuente de un determinado, y si la parte binaria estará vinculada al módulo, o si realmente está en la imagen del kernel, es necesario investigar más a fondo. El hecho de que el punto de entrada al procesamiento de esta función se enmarque de una manera especial, a través de SYSCALL_DEFINE3, indirectamente testifica a favor de la segunda opción, pero hace tiempo que entiendo que mis ideas sobre lo lógico e ilógico, lo aceptable y lo inaceptable, así como sobre lo permisible y lo inaceptable, son muy significativas. desviarse de los de los desarrolladores de L.
Nota, una piedra más en el jardín de búsqueda incorporado, al buscar una definición para esta macro, vi muchos lugares para usarla como una función, entre los cuales la definición misma de macro se escondió muy modestamente.
Por ejemplo, no entiendo por qué se necesita una utilidad externa para traducir los parámetros del formulario estándar del sistema operativo (agrc, argv) a la forma de una cadena terminada en nulo con espacios como separadores, que el módulo del sistema procesa aún más; este enfoque supera un poco al mío habilidades cognitivas Especialmente, dado el hecho de que el usuario ingresa una cadena de parámetros en forma de una cadena terminada en cero con espacios como separadores, y la utilidad en el núcleo la convierte en una forma (argc, argv). Que recuerda mucho a la vieja broma "Retiramos la tetera de la estufa, le echamos agua y tenemos un problema cuya solución ya se conoce". Y como trato de adherirme al principio “Considera a tu interlocutor no más estúpido que tú, hasta que demuestre lo contrario. E incluso después de eso, puede estar equivocado ”, y con respecto a los desarrolladores de A, la primera frase es definitivamente válida, significa que no entiendo algo, pero no estoy acostumbrado. Si alguien puede ofrecer una explicación razonable del hecho declarado de la doble conversión, entonces pido en el comentario. Pero continuamos la investigación.
Las perspectivas para la implementación de las opciones 1 y 2 se vuelven "muy débilmente visibles" (una redacción encantadora de un artículo reciente sobre las perspectivas de desarrollar ADC nacionales de alta velocidad), ya que sería muy extraño cargar un módulo en la memoria usando la función del kernel, y luego pasarle el control para implementar el kernel. función incorporada en su cuerpo. Y seguro, en el texto de la función load_module, encontramos rápidamente la llamada parse_args, parece que estamos en el camino correcto. A continuación, pasamos rápidamente por la cadena de llamadas (como siempre, veremos funciones de envoltura y macros de envoltura, pero los desarrolladores ya estamos acostumbrados a hacer la vista gorda a esas bromas tan lindas) y encontramos la función parse_one, que coloca el parámetro requerido en el lugar correcto.
Tenga en cuenta que no se verifica la validez de los parámetros, como cabría esperar, porque el núcleo, a diferencia del módulo en sí, no sabe nada sobre su propósito. Hay verificaciones de sintaxis y el número de elementos en la matriz (sí, puede haber una matriz de enteros como parámetro) y cuando se detectan errores de este tipo, la carga del módulo se detiene, pero nada más. Sin embargo, no todo se pierde, porque después de cargar el control se transfiere a la función init_module, que puede llevar a cabo la validación necesaria de los parámetros establecidos y, si
es necesario
guardar el lanzamiento , finalizar el proceso de arranque.
Sin embargo, pasamos por alto por completo la cuestión de cómo las funciones de análisis acceden a una matriz de muestras de parámetros, porque sin esto, el análisis es algo difícil. Un vistazo rápido al código muestra que se ha aplicado un truco sucio, un truco obvio: en el archivo binario, la función find_module_sections busca la sección nombrada __param, divide su tamaño por el tamaño del registro (hace mucho más) y devuelve los datos necesarios a través de la estructura. Todavía pondría las letras p delante de los nombres de los parámetros de esta función, pero esto es cuestión de gustos.
Todo parece ser claro y comprensible, lo único que le preocupa es la ausencia del atributo __initdata en los datos generados, si realmente puede permanecer en la memoria después de la inicialización, probablemente este atributo se describa en algún lugar de la parte general, por ejemplo, en los datos del vinculador, para ser honesto , perezoso para mirar, ver el epígrafe.
En resumen: el fin de semana fue útil, fue interesante entender el código fuente de L, recordar algo y aprender algo, pero el conocimiento nunca es superfluo.
Bueno, en mis suposiciones, no lo adiviné, en L se implementó una opción que resultó estar en el 7 por ciento restante, pero dolorosamente no era obvio.
Bueno, en conclusión, el grito de Yaroslavna (¿cómo podría uno prescindir de él?) ¿Por qué tengo que buscar la información necesaria (no me refiero a la cocina interna, sino a la presentación externa) de varias fuentes que no tienen estatus oficial, donde hay un documento similar al libro
“Software de una computadora. Sistema operativo funcional.
RAFOS Guía del programador del sistema ", ¿o ya no?