.. include:: ../disclaimer-sp.rst

:Original: Documentation/process/4.Coding.rst
:Translator: Carlos Bilbao <carlos.bilbao.osdev@gmail.com> and Avadhut Naik <avadhut.naik@amd.com>

.. _sp_development_coding:

Conseguir el código correcto
============================

Si bien hay mucho que decir a favor de un proceso de diseño sólido y
orientado a la comunidad, la prueba de cualquier proyecto de desarrollo del
kernel está en el código resultante. Es el código lo que será examinado por
otros desarrolladores y lo que será incluido (o no) en el árbol principal.
Por lo tanto, es la calidad de este código lo que determinará el éxito
final del proyecto.

Esta sección examinará el proceso de programación. Comenzaremos observando
algunas de las maneras en que los desarrolladores del kernel pueden cometer
errores. Luego, el enfoque se dirigirá hacia hacer las cosas bien y las
herramientas que pueden ayudar en dicha búsqueda.

Problemas
---------

Estilo de programación
**********************

El kernel ha tenido durante mucho tiempo un estilo de programación
estándar, descrito en la documentación del kernel en
`Documentation/process/coding-style.rst`. Durante gran parte de ese tiempo,
las políticas descritas en ese archivo se tomaban como, en el mejor de los
casos, orientativas. Como resultado, hay una cantidad considerable de
código en el kernel que no cumple con las pautas de estilo de programación.
La presencia de ese código lleva a dos peligros independientes para los
desarrolladores del kernel.

El primero de estos es creer que los estándares de programación del kernel
no importan y no se aplican. La realidad es que agregar nuevo código al
kernel es muy difícil si ese código no está escrito de acuerdo con el
estándar; muchos desarrolladores solicitarán que el código sea reformateado
antes de revisarlo. Una base de código tan grande como el kernel requiere
cierta uniformidad para que los desarrolladores puedan comprender
rápidamente cualquier parte de él. Así que ya no hay lugar para el código
con formato extraño.

Ocasionalmente, el estilo de programación del kernel entrará en conflicto
con el estilo obligatorio de un empleador. En tales casos, el estilo del
kernel tendrá que prevalecer antes de que el código pueda ser fusionado.
Incluir código en el kernel significa renunciar a cierto grado de control
de varias maneras, incluida la forma en que se formatea el código.

La otra trampa es asumir que el código que ya está en el kernel necesita
urgentemente correcciones de estilo de programación. Los desarrolladores
pueden comenzar a generar parches de reformateo como una forma de
familiarizarse con el proceso o como una forma de incluir su nombre en los
registros de cambios del kernel, o ambos. Pero las correcciones puramente
de estilo de programación son vistas como ruido por la comunidad de
desarrollo; tienden a recibir una recepción adversa. Por lo tanto, este
tipo de parche es mejor evitarlo. Es natural corregir el estilo de una
parte del código mientras se trabaja en él por otras razones, pero los
cambios de estilo de programación no deben hacerse por sí mismos.

El documento de estilo de programación tampoco debe leerse como una ley
absoluta que nunca puede transgredirse. Si hay una buena razón para ir en
contra del estilo (una línea que se vuelve mucho menos legible si se divide
para ajustarse al límite de 80 columnas, por ejemplo), perfecto.

Tenga en cuenta que también puedes usar la herramienta `clang-format` para
ayudarle con estas reglas, para reformatear rápidamente partes de su código
automáticamente y para revisar archivos completos a fin de detectar errores
de estilo de programación, errores tipográficos y posibles mejoras. También
es útil para ordenar `#includes`, alinear variables/macros, reformatear
texto y otras tareas similares. Consulte el archivo
`Documentation/dev-tools/clang-format.rst` para más detalles.

Algunas configuraciones básicas del editor, como la indentación y los
finales de línea, se configurarán automáticamente si utilizas un editor
compatible con EditorConfig. Consulte el sitio web oficial de EditorConfig
para obtener más información: https://editorconfig.org/

Capas de abstracción
********************

Los profesores de ciencias de la computación enseñan a los estudiantes a
hacer un uso extensivo de capas de abstracción en nombre de la
flexibilidad y el ocultamiento de la información. Sin duda, el kernel hace
un uso extensivo de la abstracción; ningún proyecto que involucre varios
millones de líneas de código podría sobrevivir de otra manera. Pero la
experiencia ha demostrado que una abstracción excesiva o prematura puede
ser tan perjudicial como la optimización prematura. La abstracción debe
usarse en la medida necesaria y ya.

A un nivel simple, considere una función que tiene un argumento que siempre
se pasa como cero por todos los que la invocan. Uno podría mantener ese
argumento por si alguien eventualmente necesita usar la flexibilidad
adicional que proporciona. Sin embargo, para entonces, es probable que el
código que implementa este argumento adicional se haya roto de alguna
manera sutil que nunca se notó, porque nunca se ha utilizado. O, cuando
surge la necesidad de flexibilidad adicional, no lo hace de una manera que
coincida con la expectativa temprana del programador. Los desarrolladores
del kernel rutinariamente enviarán parches para eliminar argumentos no
utilizados; en general, no deberían añadirse en primer lugar.

Las capas de abstracción que ocultan el acceso al hardware, a menudo para
permitir que la mayor parte de un controlador se utilice con varios
sistemas operativos, son especialmente mal vistas. Dichas capas oscurecen
el código y pueden imponer una penalización en el rendimiento; no
pertenecen al kernel de Linux.

Por otro lado, si se encuentra copiando cantidades significativas de código
de otro subsistema del kernel, es hora de preguntar si, de hecho, tendría
sentido extraer parte de ese código en una biblioteca separada o
implementar esa funcionalidad a un nivel superior. No tiene sentido
replicar el mismo código en todo el kernel.

Uso de #ifdef y del preprocesador en general
********************************************

El preprocesador de C tiene una tentación poderosa para algunos
programadores de C, quienes lo ven como una forma de programar
eficientemente una gran cantidad de flexibilidad en un archivo fuente. Pero
el preprocesador no es C, y el uso intensivo de él da como resultado un
código mucho más difícil de leer para otros y más difícil de verificar por
el compilador para su corrección. El uso intensivo del preprocesador es
asi siempre un signo de un código que necesita algo de limpieza.

La compilación condicional con `#ifdef` es, de hecho, una característica
poderosa, y se usa dentro del kernel. Pero hay poco deseo de ver código que
sté salpicado liberalmente con bloques `#ifdef`. Como regla general, el uso
de `#ifdef` debe limitarse a los archivos de encabezado siempre que sea
posible. El código condicionalmente compilado puede confinarse a funciones
que, si el código no va a estar presente, simplemente se convierten en
vacías. El compilador luego optimizará silenciosamente la llamada a la
función vacía. El resultado es un código mucho más limpio y fácil de
seguir.

Las macros del preprocesador de C presentan varios peligros, incluida la
posible evaluación múltiple de expresiones con efectos secundarios y la
falta de seguridad de tipos. Si te sientes tentado a definir una macro,
considera crear una función en línea en su lugar. El código resultante será
el mismo, pero las funciones en línea son más fáciles de leer, no evalúan
sus argumentos varias veces y permiten que el compilador realice
comprobaciones de tipo en los argumentos y el valor de retorno.

Funciones en línea
******************

Las funciones en línea presentan su propio peligro, sin embargo. Los
programadores pueden enamorarse de la eficiencia percibida al evitar una
llamada a función y llenar un archivo fuente con funciones en línea. Esas
funciones, sin embargo, pueden en realidad reducir el rendimiento. Dado que
su código se replica en cada sitio de llamada, terminan hinchando el tamaño
del kernel compilado. Eso, a su vez, crea presión en las cachés de memoria
del procesador, lo que puede ralentizar la ejecución de manera drástica
Las funciones en línea, como regla, deben ser bastante pequeñas y
relativamente raras. El costo de una llamada a función, después de todo, no
es tan alto; la creación de un gran número de funciones en línea es un
ejemplo clásico de optimización prematura.

En general, los programadores del kernel ignoran los efectos de caché bajo
su propio riesgo. El clásico intercambio de tiempo/espacio que se enseña en
las clases de estructuras de datos iniciales a menudo no se aplica al
hardware contemporáneo. El espacio *es* tiempo, en el sentido de que un
programa más grande se ejecutará más lentamente que uno más compacto.

Los compiladores más recientes toman un papel cada vez más activo al
decidir si una función dada debe realmente ser en línea o no. Por lo tanto,
la colocación liberal de palabras clave "inline" puede no solo ser
excesiva; también podría ser irrelevante.

Bloqueo
*******

En mayo de 2006, la pila de red "Devicescape" fue, con gran fanfarria,
lanzada bajo la licencia GPL y puesta a disposición para su inclusión en el
kernel principal. Esta donación fue una noticia bienvenida; el soporte para
redes inalámbricas en Linux se consideraba, en el mejor de los casos,
deficiente, y la pila de Devicescape ofrecía la promesa de solucionar esa
situación. Sin embargo, este código no fue incluido en el kernel principal
hasta junio de 2007 (versión 2.6.22). ¿Qué sucedió?

Este código mostró varios signos de haber sido desarrollado a puertas
cerradas en una empresa. Pero un problema importante en particular fue que
no estaba diseñado para funcionar en sistemas multiprocesador. Antes de que
esta pila de red (ahora llamada mac80211) pudiera fusionarse, se tuvo que
implementar un esquema de bloqueo en ella.

Hubo un tiempo en que se podía desarrollar código para el kernel de Linux
sin pensar en los problemas de concurrencia que presentan los sistemas
multiprocesador. Ahora, sin embargo, este documento se está escribiendo en
una computadora portátil con dos núcleos. Incluso en sistemas de un solo
procesador, el trabajo que se está realizando para mejorar la capacidad de
respuesta aumentará el nivel de concurrencia dentro del kernel. Los días en
que se podía escribir código para el kernel sin pensar en el bloqueo han
quedado atrás.

Cualquier recurso (estructuras de datos, registros de hardware, etc.) que
pueda ser accedido concurrentemente por más de un hilo debe estar protegido
por un bloqueo. El nuevo código debe escribirse teniendo en cuenta este
requisito; implementar el bloqueo después de que el código ya ha sido
desarrollado es una tarea mucho más difícil. Los desarrolladores del kernel
deben tomarse el tiempo para comprender bien los primitivos de bloqueo
disponibles para elegir la herramienta adecuada para el trabajo. El código
que muestre una falta de atención a la concurrencia tendrá un camino
difícil para ser incluido en el kernel principal.

Regresiones
***********

Un último peligro que vale la pena mencionar es el siguiente: puede ser
tentador realizar un cambio (que puede traer grandes mejoras) que cause un
problema para los usuarios existentes. Este tipo de cambio se llama una
"regresión", y las regresiones se han vuelto muy mal recibidas en el kernel
principal. Con pocas excepciones, los cambios que causan regresiones serán
revertidos si la regresión no se puede solucionar de manera oportuna. Es
mucho mejor evitar la regresión desde el principio.

A menudo se argumenta que una regresión puede justificarse si hace que las
cosas funcionen para más personas de las que crea problemas. ¿Por qué no
hacer un cambio si trae nueva funcionalidad a diez sistemas por cada uno
que rompe? La mejor respuesta a esta pregunta fue expresada por Linus en
julio de 2007 (traducido):

::

    Entonces, no arreglamos errores introduciendo nuevos problemas. Eso
	lleva a la locura, y nadie sabe si realmente se avanza. ¿Es dos pasos
	adelante, uno atrás, o un paso adelante y dos atrás?

(https://lwn.net/Articles/243460/).

Un tipo de regresión especialmente mal recibido es cualquier tipo de cambio
en la ABI del espacio de usuario. Una vez que se ha exportado una interfaz
al espacio de usuario, debe ser soportada indefinidamente. Este hecho hace
que la creación de interfaces para el espacio de usuario sea
particularmente desafiante: dado que no pueden cambiarse de manera
incompatible, deben hacerse bien desde el principio. Por esta razón,
siempre se requiere una gran cantidad de reflexión, documentación clara y
una amplia revisión para las interfaces del espacio de usuario.

Herramientas de verificación de código
**************************************

Por ahora, al menos, escribir código libre de errores sigue siendo un ideal
que pocos de nosotros podemos alcanzar. Sin embargo, lo que podemos esperar
hacer es detectar y corregir tantos de esos errores como sea posible antes
de que nuestro código se integre en el kernel principal. Con ese fin, los
desarrolladores del kernel han reunido una impresionante variedad de
herramientas que pueden detectar una amplia variedad de problemas oscuros
de manera automatizada. Cualquier problema detectado por el ordenador es
un problema que no afectará a un usuario más adelante, por lo que es lógico
que las herramientas automatizadas se utilicen siempre que sea posible.

El primer paso es simplemente prestar atención a las advertencias
producidas por el compilador. Las versiones contemporáneas de gcc pueden
detectar (y advertir sobre) una gran cantidad de errores potenciales. Con
bastante frecuencia, estas advertencias apuntan a problemas reales. El
código enviado para revisión no debería, por regla general, producir
ninguna advertencia del compilador. Al silenciar las advertencias, tenga
cuidado de comprender la causa real e intente evitar "correcciones" que
hagan desaparecer la advertencia sin abordar su causa.

Tenga en cuenta que no todas las advertencias del compilador están
habilitadas de forma predeterminada. Compile el kernel con
"make KCFLAGS=-W" para obtener el conjunto completo.

El kernel proporciona varias opciones de configuración que activan
funciones de depuración; la mayoría de estas se encuentran en el submenú
"kernel hacking". Varias de estas opciones deben estar activadas para
cualquier kernel utilizado para desarrollo o pruebas. En particular,
debería activar:

 - FRAME_WARN para obtener advertencias sobre marcos de pila más grandes
   que una cantidad determinada. La salida generada puede ser extensa, pero
   no es necesario preocuparse por las advertencias de otras partes del
   kernel.

 - DEBUG_OBJECTS agregará código para rastrear la vida útil de varios
   objetos creados por el kernel y advertir cuando se realicen cosas fuera
   de orden. Si está agregando un subsistema que crea (y exporta) objetos
   complejos propios, considere agregar soporte para la infraestructura de
   depuración de objetos.

 - DEBUG_SLAB puede encontrar una variedad de errores en la asignación y
   uso de memoria; debe usarse en la mayoría de los kernels de desarrollo.

 - DEBUG_SPINLOCK, DEBUG_ATOMIC_SLEEP y DEBUG_MUTEXES encontrarán una serie
   de errores comunes de bloqueo.

Hay bastantes otras opciones de depuración, algunas de las cuales se
discutirán más adelante. Algunas de ellas tienen un impacto significativo
en el rendimiento y no deben usarse todo el tiempo. Pero dedicar tiempo a
aprender las opciones disponibles probablemente será recompensado muchas
veces en poco tiempo.

Una de las herramientas de depuración más pesadas es el verificador de
bloqueos, o "lockdep". Esta herramienta rastreará la adquisición y
liberación de cada bloqueo (spinlock o mutex) en el sistema, el orden en
que se adquieren los bloqueos en relación entre sí, el entorno actual de
interrupción, y más. Luego, puede asegurarse de que los bloqueos siempre se
adquieran en el mismo orden, que las mismas suposiciones de interrupción se
apliquen en todas las situaciones, y así sucesivamente. En otras palabras,
lockdep puede encontrar varios escenarios en los que el sistema podría, en
raras ocasiones, bloquearse. Este tipo de problema puede ser doloroso
(tanto para desarrolladores como para usuarios) en un sistema desplegado;
lockdep permite encontrarlos de manera automatizada con anticipación. El
código con cualquier tipo de bloqueo no trivial debe ejecutarse con lockdep
habilitado antes de ser enviado para su inclusión.

Como programador diligente del kernel, sin duda alguna, verificará el
estado de retorno de cualquier operación (como una asignación de memoria)
que pueda fallar. Sin embargo, el hecho es que las rutas de recuperación de
fallos resultantes probablemente no hayan sido probadas en absoluto. El
código no probado tiende a ser código roto; podría tener mucha más
confianza en su código si todas esas rutas de manejo de errores se hubieran
ejercitado algunas veces.

El kernel proporciona un marco de inyección de fallos que puede hacer
precisamente eso, especialmente donde están involucradas las asignaciones
de memoria. Con la inyección de fallos habilitada, un porcentaje
configurable de las asignaciones de memoria fallarán; estas fallas pueden
restringirse a un rango específico de código. Ejecutar con la inyección de
fallos habilitada permite al programador ver cómo responde el código cuando
las cosas van mal. Consulte
Documentation/fault-injection/fault-injection.rst para obtener más
información sobre cómo utilizar esta funcionalidad.

Otros tipos de errores se pueden encontrar con la herramienta de análisis
estático "sparse". Con sparse, el programador puede recibir advertencias
sobre confusiones entre direcciones del espacio de usuario y del kernel,
mezcla de cantidades big-endian y little-endian, el paso de valores enteros
donde se espera un conjunto de banderas de bits, y así sucesivamente.
Sparse debe instalarse por separado (puede encontrarse en
https://sparse.wiki.kernel.org/index.php/Main_Page si su distribución no lo
empaqueta); luego, puede ejecutarse en el código agregando "C=1" a su
comando make.

La herramienta "Coccinelle" (https://coccinelle.gitlabpages.inria.fr/website/)
puede encontrar
una amplia variedad de posibles problemas de codificación; también puede
proponer correcciones para esos problemas. Bastantes "parches semánticos"
para el kernel se han empaquetado en el directorio scripts/coccinelle;
ejecutar "make coccicheck" ejecutará esos parches semánticos e informará
sobre cualquier problema encontrado. Consulte:
ref:`Documentation/dev-tools/coccinelle.rst <devtools_coccinelle>` para
obtener más información.

Otros tipos de errores de portabilidad se encuentran mejor compilando su
código para otras arquitecturas. Si no tiene un sistema S/390 o una placa
de desarrollo Blackfin a mano, aún puede realizar el paso de compilación.
Un gran conjunto de compiladores cruzados para sistemas x86 se puede
encontrar en

	https://www.kernel.org/pub/tools/crosstool/

Muchos sistemas de compilación disponibles comercialmente también se pueden
utilizar para compilar código de kernel para una amplia gama de
arquitecturas.

Los desarrolladores del kernel son afortunados: tienen acceso a una
variedad de herramientas de verificación de código de la que los
desarrolladores de la mayoría de los otros sistemas pueden estar celosos.
Pero todas esas herramientas no servirán de nada si no las usa. El
resultado final de ignorar estas herramientas es simple: alguien más puede
notificarle de un problema en su código a través de un "oportuno"
comentario en la lista de correo o, peor aún, el código problemático podría
ser eliminado. Es mucho más fácil usar estas herramientas en primer lugar.

Documentación
*************

La documentación a menudo ha sido más la excepción que la regla en el
desarrollo del kernel. Aun así, una documentación adecuada ayudará a
facilitar la integración de nuevo código en el kernel, hará la vida más
fácil a otros desarrolladores, y será útil para sus usuarios. En muchos
casos, la inclusión de documentación se ha vuelto esencialmente
obligatoria.

La primera pieza de documentación para cualquier parche es su changelog
asociado. Las entradas de registro deben describir el problema que se está
esolviendo, la forma de la solución, las personas que trabajaron en el
parche, cualquier efecto relevante en el rendimiento, y cualquier otra cosa
que pueda ser necesaria para entender el parche. Asegúrese de que el
changelog diga *por qué* el parche vale la pena ser aplicado; un
sorprendente número de desarrolladores no proporciona esa información.

Cualquier código que agregue una nueva interfaz para el espacio de usuario,
incluidos los nuevos archivos de sysfs o /proc, debe incluir documentación
de esa interfaz que permita a los desarrolladores del espacio de usuario
saber con qué están trabajando. Consulte `Documentation/ABI/README` para
una descripción de cómo debe formatearse esta documentación y qué
información debe proporcionarse.

El archivo
:ref:`Documentation/admin-guide/kernel-parameters.rst <kernelparameters>`
describe todos los parámetros de arranque del kernel. Cualquier parche que
agregue nuevos parámetros debe agregar las entradas correspondientes a este
archivo.

Cualquier nueva opción de configuración debe ir acompañada de un texto de
ayuda que explique claramente las opciones y cuándo el usuario podría
querer seleccionarlas.

La información de la API interna para muchos subsistemas está documentada
mediante comentarios especialmente formateados; estos comentarios pueden
extraerse y formatearse de diversas maneras mediante el script
"kernel-doc". Si está trabajando dentro de un subsistema que tiene
comentarios de kerneldoc, debe mantenerlos y agregarlos según corresponda
para las funciones disponibles externamente. Incluso en áreas que no han
sido tan documentadas, no hay ningún inconveniente en agregar comentarios
de kerneldoc para el futuro; de hecho, esta puede ser una actividad útil
para desarrolladores de kernel principiantes. El formato de estos
comentarios, junto con alguna información sobre cómo crear plantillas de
kerneldoc, se puede encontrar en
:ref:`Documentation/doc-guide/ <doc_guide>`.

Cualquiera que lea una cantidad significativa de código existente del
kernel notará que, a menudo, los comentarios son notables por su ausencia.
Una vez más, las expectativas para el nuevo código son más altas que en el
pasado; integrar código sin comentarios será más difícil. Dicho esto, hay
poco deseo de tener código excesivamente comentado. El código en sí debe
ser legible, con comentarios que expliquen los aspectos más sutiles.

Ciertas cosas siempre deben comentarse. El uso de barreras de memoria debe
ir acompañado de una línea que explique por qué la barrera es necesaria.
Las reglas de bloqueo para las estructuras de datos generalmente necesitan
explicarse en algún lugar. Las estructuras de datos importantes en general
necesitan documentación completa. Las dependencias no obvias entre
fragmentos de código separados deben señalarse. Cualquier cosa que pueda
tentar a un maintainer de código a hacer una "limpieza" incorrecta necesita
un comentario que explique por qué se hace de esa manera. Y así
sucesivamente.

Cambios en la API interna
*************************

La interfaz binaria proporcionada por el kernel al espacio de usuario no se
puede romper, excepto en las circunstancias más graves. Las interfaces de
programación internas del kernel, en cambio, son altamente fluidas y pueden
cambiarse cuando surge la necesidad. Si usted se encuentra teniendo que
hacer un rodeo alrededor de una API del kernel, o simplemente no utilizando
una funcionalidad específica porque no cumple con sus necesidades, eso
puede ser una señal de que la API necesita cambiar. Como desarrollador del
kernel, usted está autorizado a hacer esos cambios.

Hay, por supuesto, algunas condiciones. Los cambios en la API se pueden
hacer, pero necesitan estar bien justificados. Entonces, cualquier parche
que realice un cambio en la API interna debe ir acompañado de una
descripción de cuál es el cambio y por qué es necesario. Este tipo de
cambio también debe desglosarse en un parche separado, en lugar de estar
enterrado dentro de un parche más grande.

La otra condición es que un desarrollador que cambia una API interna
generalmente está encargado de la tarea de corregir cualquier código dentro
del árbol del kernel que se vea afectado por el cambio. Para una función
ampliamente utilizada, este deber puede llevar a literalmente cientos o
miles de cambios, muchos de los cuales probablemente entren en conflicto
con el trabajo que otros desarrolladores están realizando. No hace falta
decir que esto puede ser un trabajo grande, por lo que es mejor asegurarse
de que la justificación sea sólida. Tenga en cuenta que la herramienta
Coccinelle puede ayudar con los cambios de API a gran escala.

Cuando se realice un cambio incompatible en la API, siempre que sea
posible, se debe asegurar que el código que no ha sido actualizado sea
detectado por el compilador. Esto le ayudará a estar seguro de que ha
encontrado todos los usos en el árbol de esa interfaz. También alertará a
los desarrolladores de código fuera del árbol de que hay un cambio al que
necesitan responder. Apoyar el código fuera del árbol no es algo de lo que
los desarrolladores del kernel deban preocuparse, pero tampoco tenemos que
dificultarles la vida más de lo necesario.
