Principios de concurrencia: Soluciones por hardware

Este artículo es el primer manuscrito del capítulo Soluciones por hardware del libro que estoy escribiendo Principios de concurrencia para programadores. Todo el código de los ejemplos están en Github.

Soluciones por hardware

Hasta ahora hemos visto soluciones al problema de la exclusión mutua sin soporte de hardware y con solo registros de lectura-escritura atómicos, todos ellos son spinlocks pero muy ineficientes. Por un lado por el consumo de memoria, tanto para la solución de dos procesos (Dekker y Peterson) como para N procesos (Panadería) el número de registros necesarios es proporcional al número de procesos máximos que sincronizarán[31]. Esta necesidad de memoria impone una sobrecarga importante para mantener la consistencia de la memoria cache, además del consumo de CPU por la espera activa todos los procesos involucrados tienen que acceder al mismo rango de memoria que los demás. Es una penalización muy importante para sistemas con varios procesadores o núcleos. Por otro lado, si se tiene un único procesador el avance es tan lento que lo que tarda décimas en uno puede tomar horas en otros[32] en el caso de que exista mucha competencia (contention) porque varios procesos desean entrar a la sección crítica de forma casi simultánea.

Desde el inicio se buscaron soluciones por hardware que permitiesen implementar algoritmos de sincronización de forma mucho más eficiente.

Seguir leyendo

Nota rápida sobre el “no hackeo” del cifrado de Telegram

Antes que nada, no tengo ni la mínima intención de defender a Telegram, tampoco sé si sus métodos de cifrado son los mejores o no. Se trata simplemente te aclarar unos temas básicos de cifrado para el “drama” que están montando (por ejemplo) a partir del artículo original How I Hacked Telegram’s “Encryption”. (también erróneo por sensacionalista y poor ignorar de dónde está el problema, pero vaya, es cuestión de negocios, supongo).

En primer lugar, lo que ha hecho no fue hackear ningún cifrado sino usar otra debilidad del sistema operativo en algunos dispositivos para obtener acceso privilegiado (root) a todo el dispositivo. Luego, con estos privilegios pudo acceder a la memoria y ficheros de la app, algo imposible si no fuese por el bug del sistema operativo. Básicamente, no es bug de la app de Telegram, sino del sistema operativo.

Una vez que obtuvo acceso a la memoria RAM y ficheros encontró que los mensajes no están cifrados en la memoria y que la clave de cifrado de la conversación “secret” se guardan si cifrar en un fichero (privado).

Vayamos con la primer parte. El texto que introduce el usuario en la app va siempre en plano desde el “teclado” (es un software independiente) hasta la app. Es decir, es imposible evitar que exista una copia del texto plano en la memoria, siempre existirá, y siempre podrá ser detectada si se tiene acceso “root”. Además, si puede “hackear” la memoria de la app, también puede hacerlo con la del teclado, y la del sistema operativo. Es absurdo, al menos con las arquitecturas modernas, pretender que la app solucione este problema.

Por otro lado, para poder almacenar la clave de cifrado (en memoria o en el fichero) otra vez cifrada hace falta una clave privada de cifrado, por lo que esta clave debe estar almacenada y luego en memoria. Por lo tanto esta clave de cifrado de la clave de cifrado está disponible por lo que se puede obtener el texto plano de la clave de cifrado. Lo escribí así para que veáis el “bucle”, en pocas palabras: cifrar un texto con una clave cifrada que tiene que estar en la misma app no agrega nada de seguridad, sólo exige un poco más de tiempo para la primera vez que se intenta encontrar (donde “más tiempo” pueden ser un par de minutos, o menos).

Resumen:

  1. Puede ser que los tenga, pero en este caso no se detectó problemas de seguridad de la app de Telegram.
  2. Mucho menos es un hackeo a su método de cifrado, es sólo aprovecharse de un problema de seguridad no relacionado con la app.
  3. No tiene nada que ver con hackeo o debilidad el mecanismo de cifrado de Telegram. Si una app puede obtener privilegios “root” en tu móvil puede romper la seguridad de cualquier app. Estas se basan en que el sistema operativo ya brinda protección de acceso a memoria y ficheros privados. No es sólo tema de teléfonos, también de los sistemas de PCs, ¿o crees que el texto claro en ssh no pasa nunca por memoria?, ¿o que no te pueden “robar” certificados privados si no tienes los permisos correctos y el sistema operativo no los controlase?.
  4. Agregar una capa de cifrado en la app cuando la propia app debe poseer el texto claro de esa clave de cifrado no agrega seguridad, sólo una sensación de falsa seguridad.
  5. Un poco de rigor, escepticismo y opinión informada no viene nunca mal, sobre todo en temas tan complejos como cifrado y seguridad.

PIB, gasto y aumento de la deuda

El Producto Interno Bruto son mediciones estadísticas de la actividad económica de los habitantes e instituciones de un país. No hay que olvidar que es un “invento” con diferentes modos de medirlo (producción, ingresos y gastos), que la medición no puede ser perfecta (no se pude medir todo, se usan aproximaciones estadísticas), que con los cambios sociales de las últimas décadas se dejan muchos aspectos sin medir (por ejemplo no se mide el valor que aporta todo el trabajo detrás de la Wikipedia y los beneficios que genera) y tiene sus limitaciones. Aún así es una de las mejoras formas conocidas de medir la “riqueza” o actividad económica de un país que sirve analizar su evolución comparando el resultado de un período a otro. Si los datos se obtienen siempre de la misma forma dan información muy valiosa, casi imprescindible (hasta los presupuestos generales se hacen basados en estimaciones del PIB para el año correspondiente).

Seguir leyendo

Software libre y GNU/Linux en la UIB

1993

Me encargan que de la asignatura de sistemas operativas en la UIB. No teníamos ningún Unix accesible a los usuarios, tampoco teníamos PCs para instalar el novedoso GNU/Linux, todo era Macintosh. Encuentro MacMinix, un port de Minix a Mac mínimo, se ejecutaba como programa normal de Mac que abría tres consolas de shell.

Lo pruebo en los Macs del aula informática (unos 70 ordenadores) de nuestro edificio. No funcionaba el teclado español y se colgaba cada pocos segundos. Desesperación, la asignatura comenzaba en febrero de 1994, me quedaban pocos meses y no tenía una plataforma “seria” para darla.

Decido que no hay otra solución que intentar arreglar esos problemas. Pido un portátil Mac prestado al Dept. (un MacBook, me tomaba 45 minutos compilar el kernel) y me pasé unos tres meses modificando el núcleo del Minix. Descubro que el problema del teclado estaba ocasionado porque las tablas de E/S de caracteres del  Minix eran de 7 bits y se necesitaban 8 para el teclado español. Modifico todas esas funciones para trabajar con 8 bits, logro que funcione correctamente. Luego detecto que el culpable de los cuelgues era por la pila de red de Apple Talk, los ordenadores estaban conectados a una que cubría toda la universidad y claramente tenía muchas race conditions no solucionadas. Deshabilito todas esas funciones, el problema se soluciona, no tendríamos red pero tampoco era necesaria.

Así doy dos cursos, 93-94 y 94-95.

Seguir leyendo

Las cosas que no soporto que diga un programador

…y quizás tampoco las soportan los demás programadores.

En mi ordenador funciona

Si el código no funciona en un ordenador con toda las dependencias adecuadas instaladas es un error de tu programa, sin dudas, no hay excusas. Nunca digas esta frase, sólo demuestra que todavía no estás preparado ni para asumir la responsabilidad de tu propio código. Si eres alumno demuestra que no te interesa aprender sólo aprobar con el menor esfuerzo posible… además de tomar como tonto al profesor, como si nunca hubiese oído esta excusa (la oímos decenas de veces cada vez que se presentan prácticas).

El programa peta/se cuelga/no funciona ¿qué es?

Otro error típico, sobre todo de alumnos y jóvenes que preguntan al profesor o a un compañero. Si tú que conoces bien tu código no puedes encontrar el error, ¿cómo pretendes que otro te lo arregle sin conocer el mínimo detalle? Siempre tienes que ir con el código en cuestión, indicando la función o la línea donde falla y en qué condiciones. Si no lo haces así demuestra que todavía eres incapaz de analizar tu propio código. Un buen programador es capaz de analizar y estudiar cualquier código, algunos les puede costar minutos, otros muchas horas, pero es esencial saber hacerlo. Se aprende con mucha práctica, pero hay que comenzar con el código de uno mismo.

Pero si pasa los tests

Desde que se empezó a hablar de test units parece que algunos lo toman con el resultado de los dioses. Pero no, un test sólo verifica los bugs ya detectados o los que se imaginó el que los programó. Seguramente no consideró todos los posibles fallos con las diferentes condiciones que se pueden dar en el código que verifica. Sobre todo si es de un código que todavía no existe y que debe ser programado por otras personas. Por ello no es excusa que pase el test, en todo caso hay que pedir que se mejore el test con el caso descubierto en tu código. Mejor aún si tú mismo lo haces.

Eso no me lo enseñaron

No puedes ser espectador pasivo de tu aprendizaje ni esperar que los demás sean los responsables de enseñarte todo lo que necesitas aprender para cada una de tus tareas como programador. Además es imposible que en unos pocos años de estudio puedas aprender todas las cosas que necesitarás durantes las décadas futuras en tu profesión. Es cierto que muchas cosas que deberían darse no se estudian (y que también critico, a veces chillando por la universidad) pero una respuesta así indica que no asumes responsabilidad de tus conocimientos. Quizás peor, que no te enteraste qué es lo que debes aprender, un psicólogo te explicaría que es “deficiencia meta cognitiva”. En todo caso sólo di “no lo conocía” o “no se me ocurrió, ahora mismo me pongo”.

Pero el código funciona

Esta es una respuesta también muy habitual entre estudiantes y programadores novatos. Es como si un novelista respondiese a la mala crítica con un “pero si al final el mayordomo era el asesino, se entiende”. Se da en dos situaciones diferentes:

  • El código es ilegible: Hay programadores que todavía piensan que los lenguajes de alto nivel se hacen para los ordenadores no para los humanos. Pero no es así, llevamos 70 años diseñando y desarrollando lenguajes de alto nivel principalmente para facilitar el trabajo a las personas, y no sólo al programador original, sobre todo a los demás que tienen que entender o modificar el programa. Una de las condiciones fundamentales de todo programador es que su programa se legible, que sea “fácil y agradable de leer”. Hay novelas que son fáciles de leer, otras que son complejas, hay cortas y otras largas y pesadas, pero todas respetan normas básicas: sintaxis, gramática, oraciones con sustantivo verbo y objeto, limitaciones de adjetivos, puntuación, párrafos, separación entre palabras, líneas, párrafos y bordes de la página. El código fuente de un programa debe ser similar, hay normas generales (por ejemplo espaciado, sangrado, nombres de funciones variables, ficheros, etc.) y otras que son “idiomáticas” de cada lenguaje o de programadores.Lo fundamental es aprender esas normas, respetar tantos las generales como las particulares. Si no lo haces no sólo demuestra la incapacidad como programador, también una falta de respeto a los que tendrán que analizar tu código (sean estos los colegas o los profesores que te tienen que poner una nota).
  • La solución es inadecuada: Muchas veces recurren a soluciones que no sólo no son óptimas, también las peores de su clase. Por ejemplo re-implementar un ordenamiento de burbuja cuando tienen la función qsort(), o una búsqueda secuencial cuando el array está ordenado (los dos últimos son problemas de no recurrir a la menor complejidad de ejecución bien conocidas), o hacer una espera activa cuando tienen primitivas sencillas con bloqueo, etc. En la “ciencias de la computación” se estudian estos temas, algunos de ellos son fundamentales y de conocimiento obligatorio. No sirve que el programa funcione para un caso concreto, debe funcionar para todos los casos previstos y con la eficiencia “formal” bien conocida en el área. Conocer esos temas es parte del proceso de aprendizaje de un programador, si alguien te indica que la solución es errónea quizás pueda estar equivocado, pero nunca la excusa puede ser “pero funciona”.

Vaya mierda de código, debería estar programado con ponga_aquí_las_últimas_tecnologías_o_frameworks

Es el típico error que hemos cometido todos. Normalmente se hace sin tener en cuenta las condiciones del momento en que comenzó a desarrollarse, los requerimientos iniciales, la tecnología y recursos disponible, ni la multitud de limitaciones que tienen todos los proyectos. Además, el estado actual de un proyecto es la evolución durante años de programadores, tecnologías y objetivos cambiantes. Cada uno de estos elementos aporta a la deuda tecnológica, tú también generarás la misma deuda, todos los hacemos.

Así que nunca sueltes esta frase ligeramente, sobre todo si vas a una empresa, solo demostrarás que además de ignorante no tienes respeto por el trabajo de tus colegas. Quizás el código realmente sea malo, pero antes de decir una palabra espera un tiempo, aprende de su historia, y si aún así piensas que puede mejorarse propón la solución, o mejor, envía el parche.

Un secreto: cuando los programadores oímos a otro soltar esta frase de un código que ni conoce pensamos “no podía faltar el gilipollas”. No hagas de gilipollas.

Yo hubiese usado ponga_aquí_la_librería_de_moda

Si dices esto es porque alguien te preguntó que te parece lo que implementó. Decirle que hubieses usado otra librería ya no ayuda y quizás estés equivocado o no conoces todos los detalles. Es mejor analizar antes, proponer cambios concretos y reservar esta frase sólo si no hay una solución adecuada (aunque muy raro que no haya soluciones). Nunca nunca nunca sueltes esta frase sólo por ver las primeras líneas de “include” o “package”.

Yo hubiese desarrollado un nivel intermedio de servicios ponga_aquí_el_formato_de_moda

Es el típico ejemplo del programador entusiasmado que tiende a la “sobre ingeniería” y aplicar en un proyecto todas las técnicas y palabros que aprendió o leyó. Pero una una ingeniería se basa en hacer las cosas en su justa simplicidad, agregar servicios y capas intermedias con formatos de intercambio (que requieren conversiones y serializaciones/deserializaciones, antes de ayer era binario, ayer era XML, hoy es JSON, mañana quién sabe) muy pocas veces está justificado y casi nunca elimina complejidad, todo lo contrario. Lo que suelen añadir es complejidad, latencias, consumo, necesidad de recursos y más administración.

Depende del tipo de proyecto sí que es necesario desarrollar una capa de traducción de este tipo, por ejemplo cuando haces el backend/API de servidor para apps móviles. En estos casos la capa es casi un objetivo y requerimiento inicial del desarrollo, no una idea creativa para aumentar la simplicidad de desarrollo y mantenimiento.

Intenta mantener el programa lo suficientemente simple para que se cumplan los plazos y la complejidad esté controlada, si el proyecto crece y necesita agregar capas y servicios se hará en su momento. En todo caso deberías preocuparte más de la estructura de datos: que sean simples y flexibles para expandirlas y usarla de otras maneras. Y si aún así te parece que una capa intermedia con traducción es formatos es necesaria, consúltalo antes con los programadores más expertos. Nunca lo plantees como primera opción, suele generar unos :roll: importantes.

La universidad debería enseñar ponga_aquí_la_última_moda

De este tema ya escribí varias veces, la última en Lo que se aprende, o debería, en la carrera de informática, pero insisto, el tiempo es limitado, no se pueden aprender todas las tecnologías que podrías encontrarte en tu carrera profesional. Antes eran cinco años, ahora cuatro, y ya hay una ley para que sea de tres. Ni en las mejores universidades del mundo podrán enseñarte todo lo que cree importante cada uno de los “actores del mercado”, si fuese tan sencillo las currículas no serían tema de debate y cambios continuos hasta en la ACM.

Por otro lado las “tecnologías de programación” no cambian tan radicalmente, muchas veces se usan ideas que ya se estudiaron y desarrollaron antes. Por ejemplo el tema de concurrencia tan estudiado en sistemas operativos y mainframes hoy vuelve a estar de moda por los procesadores de múltiples núcleos, sí ha avanzado pero los principios son los mismos que hace 40 años. Otro ejemplo, NoSQL, las mismas técnicas de los años 70 pero usadas para tener bases de datos distribuidas mucho más limitadas que las relacionales, ¿hace falta tener una asignatura para saber cómo funcionan los hashes y su almacenamiento en fichero que viste en estructuras de datos o “algoritmia” (signifique lo que signifique)?.

¿Realmente hacen falta asignaturas obligatorias específicas sólo para “desarrollo de apps” cuando hoy lo más raro que te encuentras es que tienes que tener un thread para conectarte a un servidor o mostrar imágenes o listados largos? (ojo, sí que deben ser parte de prácticas o laboratorios, que ya se hacen en la mayoría de universidades).

¿Por qué no usaste el framework X?

Qué pesadilla, no hace falta seguir, ¿no?

Ninguno de los frameworks se ajusta a lo que necesitamos, vamos a desarrollar uno mejor

:roll: :roll: :roll: