(fuente: pexels.com)A menudo oímos, aprendemos e incluso utilizamos términos o frases que no entendemos del todo. Me parece que esto es bastante común dentro de la comunidad de desarrollo de software, ya sea RESTful Web APIs, metodología ágil, aprendizaje automático, o algún otro término. Esto no es necesariamente algo malo, pero es importante entender cuándo se conoce realmente algo y cuándo sólo se conoce el nombre para ello.
Para mí, la Programación de Sistemas es uno de esos términos. Me gustaría intentar explicar, utilizando un lenguaje sencillo, lo que significa.
Antes de que podamos entender lo que implica la Programación de Sistemas, primero tenemos que entender lo que es un Sistema. El software tiende a caer en uno de dos campos, el software de sistema y el software de aplicación.
El software de sistema es un software informático diseñado para proporcionar una plataforma a otro software. Entre los ejemplos de software de sistema se incluyen los sistemas operativos, el software de ciencia computacional, los motores de juegos, la automatización industrial y las aplicaciones de software como servicio.
… Dicho software no se considera software de sistema cuando se puede desinstalar normalmente sin que afecte al funcionamiento de otro software.
– wikipedia.org
El software de sistema es una plataforma compuesta por los programas y servicios del Sistema Operativo (SO), incluyendo la configuración y las preferencias, las bibliotecas de archivos y las funciones utilizadas para las aplicaciones del sistema. El software del sistema también incluye los controladores de dispositivos que ejecutan el hardware básico del ordenador y los periféricos.
– techopedia.com
El software del sistema se refiere a los archivos y programas que conforman el sistema operativo de su ordenador. Los archivos del sistema incluyen bibliotecas de funciones, servicios del sistema, controladores para impresoras y otro hardware, preferencias del sistema y otros archivos de configuración. Los programas que forman parte del software del sistema incluyen ensambladores, compiladores, herramientas de gestión de archivos, utilidades del sistema y depuradores.
– techterms.com
La definición de Wikipedia es muy vaga en cuanto a lo que se considera software del sistema, siempre y cuando esté proporcionando servicios a otras aplicaciones. Sin embargo, las otras dos definiciones se centran exclusivamente en el sistema operativo: controladores, kernels, bibliotecas y funciones (piense en los archivos de cabecera del kernel/libc y en los objetos compartidos). Esto implica una estrecha relación con el hardware. Si miramos otro artículo de Wikipedia sobre la programación de sistemas vemos:
La programación de sistemas requiere un gran grado de conocimiento del hardware.
El artículo continúa implicando que una parte fundamental de la programación de sistemas es la necesidad de que las cosas sean muy rápidas. Esto tiene sentido porque necesitaríamos saber mucho sobre el hardware. También tiene sentido que la velocidad (rendimiento) sea una parte central de la programación de sistemas si es una plataforma para otro software.
Si la parte más central de su aplicación (la «plataforma» de software del sistema) es lenta, entonces toda la aplicación es lenta. Para muchas aplicaciones, especialmente a escala, esto sería un acuerdo de ruptura.
El software del sistema en una cáscara de nuez
Las citas anteriores y otros recursos me han llevado a los siguientes criterios para definir el software del sistema:
- Proporciona una plataforma para que otro software se construya sobre ella.
- Interactúa directa o estrechamente con el hardware del ordenador para obtener el rendimiento necesario y exponer abstracciones (como parte de la plataforma).
Qué es y qué no es software de sistema
Ejemplos de lo que es software de sistema:
- Kernels del SO
- Drivers
- Hipervisores de metal desnudo (e.g. Hyper-V y VM Ware ESXi)
- Compiladores (que producen binarios nativos) y depuradores
Ejemplos de lo que no es software de sistema:
- Aplicación de chatGUI (Slack, Discord, etc)
- Aplicación JavaScript basada en la web
- Aplicación de servicios web
Notarás que mientras las API de servicios web proporcionan un servicio a otro software, no interactúan (típicamente) con el hardware para exponer abstracciones sobre él. Sin embargo, hay aplicaciones que caen en una zona gris intermedia. Las que me vienen a la mente son las aplicaciones de computación de alto rendimiento y el software embebido.
Las aplicaciones de computación de alto rendimiento (HPC), como el comercio en tiempo real en las bolsas de valores, no suelen exponer una plataforma, pero es común que escriban código que interactúa directamente con el hardware. Un ejemplo sería saltarse la pila de red que ofrece el kernel e implementar su propia pila de red hablando directamente con la(s) NIC(s). De esta manera podemos ver cómo el software HPC comparte muchas similitudes con el software de sistemas, al interactuar directamente con el hardware con el fin de proporcionar las ganancias de rendimiento necesarias.
El desarrollo de software embebido también comparte muchas similitudes con el software de sistemas en el sentido de que el código se escribe para interactuar directamente con el hardware. Sin embargo, cualquier abstracción proporcionada suele ser consumida por el mismo software y no podría considerarse una plataforma.
Es importante tener en cuenta las aplicaciones que comparten similitudes con nuestra definición de software de sistemas, ya que es probable que vea esas aplicaciones/trabajos descritos en estos términos (software de sistemas, ingenieros de sistemas, etc.)
Programación de Sistemas (+ Lenguajes)
Habiendo definido Sistemas, ahora podemos definir Programación de Sistemas como el acto de construir Software de Sistemas usando Lenguajes de Programación de Sistemas. Bastante sencillo, ¿verdad?
Pues hay una cosa que nos hemos saltado, los lenguajes. La gente suele hablar de los Lenguajes de Programación de Sistemas de forma que «X es genial, es rápido, compilado y un lenguaje de programación de sistemas». Pero, ¿está todo el mundo en la misma página en cuanto a lo que es un lenguaje de programación de sistemas?
Dadas nuestras definiciones de Sistemas yo definiría los criterios para un Lenguaje de Programación de Sistemas como:
- Compilado a binario nativo
- Puede ser construido sin dependencias de otro software (incluyendo un kernel)
- Características de rendimiento similares a las de otros lenguajes de programación de sistemas
Disclaimer: Esta es mi definición. Dado que no hay un criterio establecido, estoy derivando una definición de lo que tiene sentido para mí dado el contexto en el que he definido el software del sistema.
Compilar a binario nativo
Si un lenguaje no puede compilar a un ejecutable que sea directamente interpretable por la CPU entonces, por definición, se está ejecutando en una plataforma (por ejemplo, JVM, Ruby VM, Python VM, etc). Puede haber algunos argumentos para hacer aquí, pero para la simplicidad creo que este es un criterio adecuado.
Sin dependencias
El argumento es similar a la compilación a un binario nativo. Si el lenguaje siempre requiere que algún otro software esté presente para ejecutarse, entonces se está ejecutando en una plataforma. Un ejemplo de esto es Go y su biblioteca estándar incluida. Requiere el apoyo del sistema operativo para realizar acciones básicas como la asignación de memoria, la generación de hilos (para que las goroutines se ejecuten), para su poller de red incorporado, y otras acciones. Aunque es posible reimplementar estas funciones básicas, crea una barrera para su uso en este contexto y es fácil imaginar por qué no todos los lenguajes, incluso los que compilan a binarios estáticos, están pensados como lenguajes de programación de sistemas.
Características de Rendimiento Similares
Este es un poco de evasión. Sin embargo, se trata de decir que dentro del sistema de lenguajes típicamente clasificados como lenguajes de programación de sistemas, no debería haber grandes (orden de magnitudes) diferencias en las características de rendimiento. Por características me estoy refiriendo explícitamente a la velocidad de ejecución y a la eficiencia de la memoria.
El estándar de oro para la comparación es C y/o C++, como se representa a menudo en los benchmarks comparativos, que miden la velocidad de ejecución en cuántos órdenes de magnitud son los lenguajes más lentos que C/C++.
Nombrando algunos
Los lenguajes que vienen a la mente inmediatamente, dada la definición anterior son C y C++. Pero también hay lenguajes más nuevos como Rust y Nim que también llenan este nicho. De hecho, ya existe un sistema operativo escrito íntegramente en Rust (RedoxOS) y un kernel en Nim (nimkernel).
Hablemos de Go
Antes he insinuado que Go podría no entrar en la familia de los «lenguajes de programación de sistemas». Sin embargo, al igual que no todas las aplicaciones encajan bien en el software de aplicación y el software de sistema, tampoco lo hacen los lenguajes.
A menudo la gente llama a Go un lenguaje de programación de sistemas e incluso golang.org se cita como:
Go es un lenguaje de propósito general diseñado con la programación de sistemas en mente.
Sin embargo, incluso esto no es una afirmación rotunda de que Go es un lenguaje de programación de sistemas, simplemente que está diseñado con él en mente. Me parece que se encuentra más bien en el medio.
Aunque Go compila a binarios nativos, contiene conceptos útiles de bajo nivel (punteros crudos/inseguros, tipos nativos como bytes e int32, y soporte de ensamblaje en línea), y es relativamente performante; todavía tiene algunos desafíos que superar. Go viene con un tiempo de ejecución y un recolector de basura.
Un tiempo de ejecución significa que se necesitará un bootstrapping/overriding del tiempo de ejecución para funcionar en entornos sin kernels. Esto se adentra más en la implementación interna del lenguaje, que podría cambiar en futuras versiones. Los cambios requieren un trabajo adicional de bootstrapping a medida que el lenguaje evoluciona.
Un recolector de basura (GC) significa que Go está restringido en qué dominios de aplicación puede ser utilizado o que el GC debe ser desactivado y reemplazado con la gestión manual de la memoria. En el caso de que el GC no pueda ser reemplazado, el dominio de tiempo real (definido por las operaciones que deben completarse dentro de determinados límites de tiempo y/o el rendimiento se mide en nano-segundos) no podría arriesgarse a los tiempos de pausa no deterministas de un GC.
Software de Sistemas Distribuidos
Con el aumento de la charla de los sistemas distribuidos, y aplicaciones como Kubernetes llegando a ser muy popular, llegamos a escuchar una gran cantidad de nuevo vocabulario que (si somos honestos) la mayoría de nosotros no entendemos completamente.
Hasta ahora, he visto los términos programación de sistemas e ingenieros de sistemas utilizados en contextos en los que lo que realmente querían decir era programación de sistemas distribuidos e ingenieros de sistemas distribuidos.
Hemos definido el software de sistemas, los lenguajes de sistemas y la programación de sistemas en este post. Sin embargo, cuando hablamos de sistemas distribuidos, el significado de sistema cambia. Y aunque no voy a sumergirme en las diferencias específicas aquí (sobre todo porque todavía tengo que entenderlas mejor yo mismo), es importante que hagamos esas distinciones mentales y utilicemos un discurso más exacto cuando podamos para evitar confusiones a los que todavía están aprendiendo el espacio.