¿Qué factores sociales o técnicos condujeron al surgimiento de la mentalidad CIY?
La causa raíz es obviamente la razón técnica: la portabilidad binaria es más difícil que la portabilidad de origen . Fuera de los paquetes de distribución, la mayoría del software gratuito solo está disponible en forma de fuente porque es mucho más conveniente para los autores / mantenedores.
Hasta que las distribuciones de Linux comenzaron a empaquetar la mayoría de las cosas que la gente promedio querría usar, su única opción era obtener la fuente y compilarla para su propio sistema. Los proveedores comerciales de Unix generalmente no incluían cosas que casi todos querían (por ejemplo, un buen shell como GNU bash
o similar), solo su propia implementación sh
y / o csh
, por lo que necesitabas construir cosas tú mismo si (como administrador del sistema) para proporcionar un entorno agradable de Unix a sus usuarios para uso interactivo.
La situación ahora, con la mayoría de las personas siendo el único administrador y el único usuario de la máquina sentada en su escritorio, es muy diferente del modelo tradicional de Unix. Un administrador de sistemas mantuvo el software en el sistema central y en el escritorio de todos. (A menudo, haciendo que las estaciones de trabajo de la gente solo monten NFS /opt
y /usr/local/
desde el servidor central, e instalen cosas allí)
Antes de cosas como .NET y Java, la verdadera portabilidad binaria en diferentes arquitecturas de CPU era imposible. La cultura de Unix evolucionó con la portabilidad de origen como predeterminada por este motivo, con poco esfuerzo incluso para intentar habilitar la portabilidad binaria hasta los esfuerzos recientes de Linux como LSB. Por ejemplo, POSIX ( el principal estándar de Unix) solo intenta estandarizar la portabilidad de origen, incluso en versiones recientes.
Factor cultural relacionado: los primeros comerciales de AT&T Unix venían con código fuente (en cintas). No tenía que construir el sistema desde la fuente, solo estaba allí en caso de que quisiera ver cómo funcionaba realmente algo cuando los documentos no eran suficientes.
Wikipedia dice :
"La política de Unix de una extensa documentación en línea y (durante muchos años) acceso inmediato a todo el código fuente del sistema aumentó las expectativas del programador y contribuyó al lanzamiento en 1983 del movimiento de software libre".
No estoy seguro de qué motivó esta decisión, ya que dar a los clientes acceso al código fuente del software comercial es inaudito en estos días. Claramente, hay algunos sesgos culturales tempranos en esta dirección, pero tal vez eso surgió de las raíces de Unix como un sistema operativo portátil escrito principalmente en C (no en lenguaje ensamblador) que podría compilarse para hardware diferente. Creo que muchos sistemas operativos anteriores tenían más de su código escrito en asm para una CPU específica, por lo que la portabilidad a nivel de origen fue uno de los puntos fuertes de Unix. (Puedo estar equivocado sobre esto; no soy un experto en Unix temprano, pero Unix y C están relacionados).
La distribución de software en forma de fuente es, con mucho, la forma más fácil de permitir que las personas lo adapten a cualquier sistema en el que quieran que se ejecute. (Ya sea usuarios finales o personas que lo empaquetan para una distribución de Linux). Si el software ya ha sido empaquetado por / para una distribución, los usuarios finales pueden usarlo.
Pero es demasiado esperar que los autores de la mayoría de los paquetes creen binarios para cada sistema posible. Algunos proyectos importantes proporcionan binarios para algunos casos comunes (especialmente x86 / windows donde el sistema operativo no viene con un entorno de compilación, y el proveedor del sistema operativo ha puesto un gran énfasis en la distribución de instaladores solo binarios).
Hacer que un software se ejecute en un sistema diferente del que utilizó el autor podría incluso requerir algunos pequeños cambios, que son fáciles con la fuente . Un pequeño programa único que alguien escribió para rascarse su propio picor probablemente nunca se haya probado en la mayoría de los sistemas oscuros. Tener la fuente hace posible hacer tales cambios. El autor original podría haber pasado por alto algo o haber escrito intencionalmente menos código portátil porque le ahorró mucho tiempo. Incluso los paquetes principales como Info-ZIP no tenían probadores en todas las plataformas de inmediato, y necesitaban que las personas enviaran sus parches de portabilidad a medida que se descubrían problemas.
(Hay otros tipos de problemas de portabilidad a nivel de origen que solo suceden debido a diferencias en el entorno de compilación, y no son realmente relevantes para el problema aquí. Con la portabilidad binaria al estilo Java, las herramientas automáticas ( autoconf
/ auto-make
) y cosas similares como cmake
wouldn no será necesario. Y no tendríamos cosas como algunos sistemas requieren la inclusión de en <netinet/in.h>
lugar de<arpa/inet.h>
forntohl(3)
. (¡Y tal vez no tendríamos ntohl()
ni ninguna otra cosa de orden de bytes en primer lugar!)
Me desarrollo regularmente en lenguajes .NET, por lo que no soy analfabeto informático.
Compilar una vez, ejecutar en cualquier lugar es uno de los principales objetivos de .NET y también de Java, por lo que es justo decir que se han inventado lenguajes completos en un esfuerzo por resolver este problema , y su experiencia de desarrollo es con uno de ellos. Con .NET, su binario se ejecuta en un entorno de tiempo de ejecución portátil (CLR) . Java llama a su entorno de tiempo de ejecución la Máquina virtual Java . Solo necesita distribuir un binario que funcione en cualquier sistema (al menos, cualquier sistema en el que alguien ya haya implementado un JVM o CLR). Todavía puede tener problemas de portabilidad como, /
vs \
separadores de ruta, o cómo imprimir, o detalles de diseño de GUI, por supuesto.
Una gran cantidad de software está escrito en idiomas que están completamente compilados en código nativo . No hay .net
código de bytes o Java, solo código de máquina nativo para la CPU en la que se ejecutará, almacenado en un formato de archivo ejecutable no portátil. C y C ++ son los principales ejemplos de esto, especialmente en el mundo de Unix. Obviamente, esto significa que se debe compilar un binario para una arquitectura de CPU específica.
Las versiones de la biblioteca son otro problema . Las bibliotecas pueden y a menudo mantienen estable la API de nivel fuente mientras cambian la ABI de nivel binario. (Consulte Diferencia entre API y ABI ). Por ejemplo, agregar otro miembro a un opaco struct
todavía cambia su tamaño y requiere una recompilación con encabezados para la nueva versión de la biblioteca para cualquier código que asigne espacio para dicha estructura, ya sea dinámico (malloc ), estático (global) o automático (local en la pila).
Los sistemas operativos también son importantes . Un sabor diferente de Unix para la misma arquitectura de CPU puede tener diferentes formatos de archivo binario, un ABI diferente para hacer llamadas al sistema, y diferentes valores numéricos de las constantes como fopen(3)
's O_RDONLY
, O_APPEND
,O_TRUNC
.
Tenga en cuenta que incluso un binario vinculado dinámicamente todavía tiene algún código de inicio específico del sistema operativo que se ejecuta antes main()
. En Windows, esto es crt0
. Unix y Linux tienen lo mismo, donde algunos códigos de inicio de C-Runtime están vinculados estáticamente en cada binario. Supongo que, en teoría, podría diseñar un sistema donde ese código también se vincule dinámicamente, y sea parte de libc o del enlazador dinámico en sí, pero no es así como funcionan las cosas en la práctica en cualquier sistema operativo que conozca. Eso solo resolvería el problema ABI de llamada al sistema, no el problema de los valores numéricos para las constantes de las funciones de la biblioteca estándar. (Normalmente, las llamadas al sistema se realizan a través de las funciones de contenedor libc: un binario Linux x86-64 normal para la fuente que usa mmap()
no incluirá la syscall
instrucción, solo uncall
instrucción para la función de contenedor libc del mismo nombre.
Esto es parte de por qué no puede simplemente ejecutar binarios i386-FreeBSD en i386-Linux. (Durante un tiempo, el kernel de Linux tenía una capa de compatibilidad de llamadas al sistema. Creo que al menos uno de los BSD puede ejecutar binarios de Linux, con una capa de compatibilidad similar, pero por supuesto necesita bibliotecas de Linux).
Si quisiera distribuir binarios, necesitaría hacer uno separado para cada combinación de CPU / OS-flavour + versión / instalado-biblioteca-versiones .
En los años 80 y 90, había muchos tipos diferentes de CPU de uso común para sistemas Unix (MIPS, SPARC, POWER, PA-RISC, m68k, etc.), y muchos sabores diferentes de Unix (IRIX, SunOS, Solaris, AIX, HP-UX, BSD, etc.).
Y eso es solo sistemas Unix . Muchos paquetes fuente también compilan y funcionan en otros sistemas, como VAX / VMS, MacOS (m68k y PPC), Amiga, PC / MS-DOS, Atari ST, etc.
Todavía hay muchas arquitecturas de CPU y sistemas operativos, aunque ahora la gran mayoría de los equipos de escritorio son x86 con uno de los tres sistemas operativos principales.
Por lo tanto, ya hay más combinaciones de CPU / OS de las que puede sacudir, incluso antes de comenzar a pensar en las dependencias de bibliotecas de terceros que pueden estar en diferentes versiones en diferentes sistemas. (Todo lo que no esté empaquetado por el proveedor del sistema operativo debería instalarse a mano).
Cualquier ruta que se compila en el binario también es específica del sistema. (Esto ahorra RAM y tiempo en comparación con leerlos desde un archivo de configuración al inicio). Los sistemas Unix de la vieja escuela generalmente tenían muchas cosas personalizadas a mano, por lo que no hay forma de que puedas hacer suposiciones válidas sobre qué es dónde.
La distribución de binarios era totalmente inviable para la Unix de la vieja escuela, excepto para los grandes proyectos comerciales que pueden permitirse construir y probar todas las combinaciones principales .
Incluso hacer binarios para justos i386-linux-gnu
y amd64-linux-gnu
es difícil. Se ha dedicado mucho tiempo y esfuerzo a cosas como Linux Standard Base para hacer posibles los archivos binarios portátiles . Incluso la vinculación estática de binarios no resuelve todo. (p. ej., ¿cómo debería imprimirse un programa de procesamiento de texto en un sistema RedHat frente a un sistema Debian? ¿Cómo debería la instalación agregar un usuario o grupo para un demonio y organizar su script de inicio para que se ejecute después de cada reinicio?) ejemplos, porque recompilar desde la fuente no los resuelve.
Además de todo eso, en el pasado la memoria era más preciosa de lo que es ahora. Dejar de lado las características opcionales en tiempo de compilación puede crear binarios más pequeños (menos tamaño de código) que también usan menos memoria para sus estructuras de datos. Si una característica requirió un miembro adicional en cada instancia de un determinado class
o struct
para rastrear algo, deshabilitar esa característica reducirá el objeto en 4 bytes (por ejemplo), lo cual es bueno si es un objeto al que el programa asigna 100k.
Las características de tiempo de compilación opcionales actualmente se usan con mayor frecuencia para hacer que las bibliotecas adicionales sean opcionales. por ejemplo, puede compilar ffmpeg
con o sin libx264
, libx265
, libvorbis
, y muchas otras bibliotecas de vídeo específico / codificadores de audio, el manejo de los subtítulos, etc. etc. Más comúnmente, muchas cosas pueden ser compilados con o sin libreadline
: si está disponible cuando se ejecuta ./configure
, el El binario resultante dependerá de la biblioteca y proporcionará una edición de línea elegante al leer desde un terminal. Si no es así, entonces el programa utilizará algún soporte alternativo para simplemente leer líneas del stdin con fgets()
o algo así).
Algunos proyectos aún usan características opcionales para omitir el código innecesario por razones de rendimiento. por ejemplo, el kernel de Linux en sí mismo puede construirse sin compatibilidad con SMP (por ejemplo, para un sistema integrado o un escritorio antiguo), en cuyo caso gran parte del bloqueo es más simple. O con muchas otras características opcionales que afectan parte del código central, no solo omitiendo controladores u otras características de hardware. (Aunque las opciones de configuración específicas del arco y del hardware representan una gran parte del código fuente total. Consulte ¿Por qué el kernel de Linux tiene más de 15 millones de líneas de código? )