Archivo

Archive for the ‘programación’ Category

Las elecciones de Podemos, voto electrónico, y AgoraVoting (y 2)

noviembre 4, 2014 16 comentarios

Hace unos días escribí un apunte sobre las tan comentadas elecciones electrónicas de Podemos. Comencé el artículo con una descripción de los requisitos que tiene que tener un sistema de votación (de papeleta y electrónico) y el procedimiento que se seguía en las votaciones tradicionales para asegurar que se cumplan esos requisitos (voto secreto y anónimo, no adulteración, impedir agregar votos, auditoría…).

En ese apunte me limité a comentar el problema del censo de Podemos, y cómo sólo eso hacía que se incumpliesen varios de los requisitos mencionados anteriormente. Aquí introduzco un leve paréntesis para dejar claras algunas cosas (lo pongo “entrecomillado” para que te lo puedas saltar).

Leer más…

Ciencias de la computación e ingeniería, y [algunas de] sus diferencias

octubre 7, 2014 17 comentarios

Hay un tema que se estudia en todas las carreras de informática, el de “sincronización, concurrencia y exclusión mutua entre procesos”. Surgió en la década de 1950 cuando empezaron a desarrollarse los primeros sistemas con multiprogramación. Aunque surgió como problemática de sistemas operativos se ha convertido en un tema de general y fundamental en informática. Todas las carreras de informática incluyen su estudio tanto en las asignaturas de sistemas operativos como en otras específicas de programación concurrente.

El tema de concurrencia (y su hermano más complejo “programación distribuida”) es un típico tema de “ciencias de la computación”, donde se estudian los algoritmos y se prueban formalmente que funcionan: se demuestra que aseguran exclusión mutua, que no produce inanición (starvation), ni interbloqueos (deadlocks). No es simple enseñar estos temas y sus soluciones obligan a pensar de otro manera, un programa ya no es una serie secuencial de instrucciones, sino que se mete por medio una intercalación “no controlada” y no determinística de otras instrucciones de procesos independientes.

El tema de exclusión mutua es un tema relativamente sencillo de demostrar, por ejemplo este código que suma una variable desde cero hasta 99.999.999 (cien millones menos uno), pero no lo hace de una forma tradicional, sino que son dos hilos (threads) los que la incrementan individualmente. Podéis compilarlo y probarlo (no olvidéis de compilarlo con la opción -pthread), veréis que produce unos errores enormes. El problema es que dos procesos diferentes acceden al mismo recurso compartido (la variable count) y se “pierden” operaciones debido a las interrupciones e intercalaciones.

Este caso es muy estudiado y todas las asignaturas comienzan con algo similar, es la exclusión mutua entre [solo] dos procesos. Así se estudian los “cuatro intentos” para solucionarlo, luego el algoritmo de Dekker y finalmente el de Peterson (aquí los tenéis a todos).

Así se puede demostrar formalmente que funciona correctamente, que asegura exclusión mutua y tal y cual. Pero si lo implementas (aquí lo tenéis listo para compilar y ejecutar con el ejemplo anterior) y lo pruebas en cualquier ordenador moderno verás que… falla como escopeta de feria.

¿Qué pasó? ¿Falla la teoría? ¿Fallan las demostraciones? ¿Los teóricos de las ciencias de la computación no sirven para nada?

Ninguna de ellas, este es un típico ejemplo donde la teoría se distanció de la “realidad”, o mejor dicho, donde la evolución tecnológica de los microprocesadores modernos hace que las técnicas de concurrencia que dábamos por buenas (y que lo son formalmente y con todos los modelos de computación y/o de lenguajes “tradicionales”) ya no funcionen. Es la diferencia entre hacer “sólo ciencia” y la “ingeniería” (es decir, resolver problemas que te impone la realidad, no tan perfecta como la teoría).

Tampoco es para los ingenieros se feliciten y miren por encima del hombro a los teóricos, porque la mayoría de “ingenieros” tampoco saben explicar o resolver este problema, muchas veces ni siquiera son capaces de identificarlo (no, si estás leyendo este blog no eres de la “media”, eres bastante friki y al menos lo habrás oído, o sabrás buscar rápidamente en la Wikipedia o Stackoverflow ;) ).

Si no tienes idea de qué está pasando, ya lo explicaré en un comentario (si nadie lo hace antes), pero mientras tanto puedes ir probando este otro código que sí funciona y sólo difiere en una línea con el anterior.

Sólo quería recordar que aún en ciencias tan “formales” como la informática la teoría no siempre funciona en la práctica, que el tema de concurrencia tiene sus complejidades no siempre a la vista, que un buen ingeniero debe saber reconocer esos problemas y poder solucionarlo, y que esto exige que conozcamos bastante de detalles del hardware y microprocesadores actuales.

No es nada simple, lleva su tiempo, y tendemos a pensar que lo sabemos todo. O que la “ingeniería informática” consiste en saber liderar sprints de Scrums con postits de colores fosforitos.

PS: Por estas cosas, entre muchas, la informática es apasionante.

La compleja coreografía porque presionaste la letra A

abril 29, 2014 19 comentarios

Tu teclado detectó que presionaste la letra A, sus chips lo codifican en un scancode, este código es convertido en un señales eléctricas que se transmitirán por el cable USB al controlador que está conectado a la placa del ordenador. Este controlador detecta las señales eléctricas, las reconstruye en números binarios que almacena en un área de memoria del dispositivo, a continuación genera una solicitud de interrupción (IRQ) que será transmitida al microprocesador.

Esta interrupción está codificada y se recibe en el procesador en unas pins especiales para ellas, cuando llega la generada por la tecla que presionaste el procesador decide a qué core o procesador enviar esa petición (enrutamiento de interrupciones). Éste interrumpe lo que estaba ejecutando en ese momento,  analiza el IRQ, accede a una tabla de interrupciones (que fue rellenada cuando el sistema operativo se inició) donde le indica la dirección de la rutina del kernel que debe ejecutar. Cambia los registros necesarios, posiblemente invalida (flush) las cache y TLB del proceso anterior, cambia el nivel de ejecución del procesador a uno de con más privilegios y pasa a ejecutar la dirección indicada en la tabla de interrupciones.

La rutina del núcleo del sistema operativo analiza los registros y llama al gestor del controlador USB, que puede acceder a la memoria del dispositivo vía instrucciones de E/S del procesaor para que copie los datos a la memoria RAM. También llamará al gestor específico de teclado por USB (lo más probable es que sea el usbhdi) que convierte el scancode original en un código de caracteres vía una tabla de conversión (o mapa del teclado).

Una vez realizada las operaciones de transferencia de datos desde el dispositivo, se llaman a las rutinas de E/S de caracteres del núcleo. Éstas analizan qué proceso es el que debe recibir esa entrada de teclado, si usas un GNU/Linux con interfaz gráfica el proceso es el servidor X (X.org), copian los datos al área de memoria de dicho proceso, lo desbloquean y llaman al scheduler para que decida qué proceso debe ejecutar a continuación.

Al desbloquearse el proceso, éste pasa de la lista de procesos bloqueado a la lista de procesos listos para ejecutar. Allí el servidor X competirá por el procesador con otros procesos, eventualmente será seleccionado por el scheduler, este procederá a preparar al procesador (o núcleo) para que lo ejecute (cambio de contexto o context switch), invalidará las caches del proceso anterior, preparará las tablas de páginas básicas, cambia el privilegio del procesador al de uno de proceso normal y finalmente transfiere el control al proceso X.

Éste continúa su ejecución desde la llamada epoll o select que hizo para recibir E/S, analiza los datos que le dejó el sistema operativo y decide que es un letra picada en el teclado (el editor de texto, o la terminal, o el navegador web….), analiza cuál era el proceso interactivo que tiene la ventana activa en ese momento, codifica el evento en el protocolo X11, y se lo envía a dicho proceso vía memoria compartido o socket UNIX.

Al enviar el mensaje a otro proceso, se llama otra vez a una rutina del sistema operativo en un proceso similar al IRQ inicial, pero esta vez iniciado por una instrucción especial (interrupción por software) que hace que el procesador la trate de forma similar, selecciona un procesador para que la trate, analiza el código de interrupción y los registros que dejó el programa, cambia a modo privilegiado y llama a la rutina del kernel que tratará esta interrupción (posiblemente las de UNIX socket).

Esta rutina mira en las tablas de sockets cuál es el proceso receptor (el editor, terminal, navegador…), copia los datos necesarios, desbloquea al proceso moviéndolo a la cola de listo para ejecutar y llama al scheduler.

Eventualmente el proceso que debe recibir esa letra A es seleccionado por el scheduler, pasa a ejecución, continúa su ejecución desde el select o epoll, analiza la entrada, decide que hay que mostrarlo en pantalla, codifica la información necesaria (caracter, tipo de letra, posición, color, etc.) y la envía nuevamente -mediante un mensaje en protocolo X11- al servidor X.

Se repite de nuevo el proceso, interrupción de software, llamada a una rutina del núcleo y desbloqueo del servidor X que eventualmente es ejecutado.

Éste analiza el mensaje, detecta que tiene que dibujar una letra en la pantalla y llama a sus rutinas de dibujo de fuentes TrueType. Estas rutinas recuperan la información de la fuente necesaria (consisten de puntos en 2D), aplican los fórmulas necesarias que definen cómo debe dibujarse en la pantalla y llaman a las rutinas de DRI del kernel que lo harán, vía ayuda del gestor de la placa gráfica, en un complicado procedimiento de sincronización entre el servidor X, el gestor de la placa y la propia placa gráfica (que es otro ordenador muy potente y complejo, con su propio “sistema operativo”).

La letra A es dibujada así en la memoria del back buffer de la tarjeta, también se dibujan las otras ventanas con complicadas combinaciones y copias (compositing) para intentar minimizar todo lo que hay que re-dibujar. Cuando el back buffer está completo, se notifica a la placa gráfica que lo intercambie con el front buffer (lo que se visualiza por la pantalla), ésta espera que llegue el momento justo de sincronización con el monitor (para que no parpadee con mezcla de imágenes de ambos buffers) y finalmente hace el cambio y puedes ver lo que esperabas:

La letra A

Esta maravillosa coreografía de sincronización y paso de información ocurre cada vez que presionas una tecla, o mueves el ratón un pixel, o se empieza a bajar una imagen de la web. Y no sólo en tu PC o Mac, ocurre lo mismo en tu teléfono móvil, tu router WiFi, tu lector de libros, o tu smartwatch.

Todo esto que acabo de explicar ya funcionaba prácticamente igual desde finales de los años 70. Explico estas interacciones y algoritmos en mis clases de sistema operativo desde hace más de 20 años, pero nunca deja de maravillarme al nivel de complejidad y sofisticación al que hemos llegado en pocas décadas de informática.

 

 

 

Una técnica útil para el programador solitario (o sea, yo)

abril 24, 2014 13 comentarios

No sé si os pasa a todos, yo creo que sí, pero tenía serios problemas de organización y productividad en el desarrollo de Menéame. Quizás sea un caso especial, es un sistema complejo:

  • software relativamente grande y soy básicamente el único programador responsable de todos los módulos,
  • diversidad de lenguajes, PHP para la web, Python para scripts y programas “off line”, Perl por herencia de hace años,
  • base de datos grande e imposible de hacer alteraciones de las tablas por el tamaño de las mismas (ya veremos con el MySQL 5.6),
  • mucha manipulación de datos en la base de datos,
  • interacciones complejas de usuarios,
  • muchos controles de entradas y acciones de usuarios “externos”,
  • los usuarios piden muchas modificaciones, correcciones y detectan bugs que ni se te pasaron por la cabeza que podían ocurrir,
  • cada vez que se implementa una nueva característica (cada vez más complejas), aparecen nuevos bugs y sobre todo, los usuarios demandan muchas modificaciones y ań nuevas características complementarias.

Con todo esto, a veces me ocurría que me bloqueaba porque no sabía por dónde comenzar, o que pasos seguir, o cuál era el trabajo pendiente, cuáles son importantes, cuáles son urgentes, y cuáles secundarios que pueden esperar hasta tener ese momento de inspiración. Soy bastante desorganizado, y odio profundamente usar programas (los tipos “gestión de tickets”) para esto. Ya demasiado tengo con ventanas de editores, consolas de administración y páginas de manuales para encima tener que estar buscando una ventana perdida para ir leyendo y apuntando lo que estoy haciendo.

Leer más…

Tendencias (“trends”) históricas del uso de palabras con Sphinx

febrero 13, 2014 4 comentarios

Hace unos días quise saber desde cuándo se empezó a hablar de desahucios y suicidios en la prensa en España. Fui a Google Trends y el gráfico mostraba una evolución demasiada plana, que no se correspondía con tantas noticias que leímos en la prensa española. Me pregunté si, y cómo, podía obtener esas estadísticas en Menéame. Se me ocurrió que debería haber un truco relativamente sencillo usando los índices de de búsqueda de Sphinx (lo usamos para el buscador de Menéame). Así fue que en pocas horas pude implementar un sistema similar a Google Trends en Menéame.

Esto es lo que salió con las tendencias de esas dos palabras por su frecuencia de aparición por meses:

Evolución de suicidios y deshaucios

Leer más…

No basta con ser ingeniero (y II), un ejemplo práctico

noviembre 30, 2013 7 comentarios

Hace unos días ya comenté el problema de que eliminaron una función importante en KitKat con la excusa de que no estaba documentada y por lo tanto “no oficial”, pero es una limitación seria para millones de usuarios y casi una traición para miles de desarrolladores. Hoy veo el siguiente comentario en el hilo del bug:

You broke file-uploading and released 4.4 without a ready replacement or workaround? O_o

So even if app devs manage to hack around it, they’ll have to maintain the hack forever to support phones stuck on 4.4 even after 4.5+ introduces a proper replacement function?

I need a Kitkat bar.

Es una observación clave.

Una pésima decisión de los ingenieros tendrá consecuencias negativas a largo plazo para desarrolladores, usuarios, y hasta toda la plataforma. No lo meditaron, todavía no se han percatado que las decisiones de ingenieros tienen efectos muy reales sobre las personas. Por eso, para ser un ingeniero hay que ser más que un ingeniero.

De nuevo queda en evidencia que los programadores vamos muy justitos en el tema, incluso para el nivel de programadores del sistema Android. Me genera una mezcla de decepción y cabreo, y me hace admirar cada vez más las “visión social” a largo plazo de Linus Torvalds: literalmente se enfurece cuando alguien le propone cambiar la interfaz (ABI) del kernel Linux que genera incompatibilidades con aplicaciones existentes, aunque tengan más de 10 años. Eso es ser más que un ingeniero.

PS: La única opción para remediar el problema es que parcheen las librerías de Android Kitkat para re-implementar la función openFileChooser() que eliminaron. Otra cosa es que asuman el error y lo hagan. Lo dudo, ojalá esté equivocado.

Android KitKat… ¿pretenden matar el HTML5 en Android?

noviembre 27, 2013 13 comentarios

La pregunta no es tan sensacionalista como parece. Hace unos días me avisaron de problemas para subir fotos a Menéame con Android KitKat. Era muy raro que diese problemas, está todo programado respetando estándares de HTML5 y Javascript. Descubrí que ese caso se debía a problemas de la gestión de memoria de Android y cómo la trataba Chrome (al ejecutar otro programa para seleccionar la imagen, si necesita memoria se la quita al Chrome, por lo que hay un “cambio de configuración”, al volver a Chrome, éste recarga la página completa, en vez de “reconstruir” lo que tenía). Pero descubrí otro error aún peor y que sucede siempre: es imposible subir una fotografía. Lo probé con el Nexus 4 y con el 7, en ambos el mismo problema, da un error “fatal” al seleccionar cualquier foto.

Lo probé con el Firefox sobre los mismos dispositivos, y no hubo problemas (además de sorprenderme la velocidad de Firefox, ¡cómo ha mejorado!). Se puede observar el fallo (“Error abrir archivo seleccionado” [sic]) en el siguiente vídeo que hice en el Nexus 4:

Visto lo visto, y que no parecía simple de solucionar, además que quería solucionar lo de compartir más fácilmente a Menéame desde un móvil, me decidí a desarrollar una pequeña app:

app Menéame de pruebas

Esta app sólo crea una actividad, basada en WebView, que empotra el renderer HTML. Hasta la versión 4.3 de Android, este renderer era el del “webkit” básico, a partir de 4.4 (KitKat) ya se usa el Chromium (el proyecto libre del Chrome). Todo iba bien, y funcionaba muy rápido… hasta que probé la subida de ficheros: imposible, no funciona en KitKat, y es imposible arreglarla (al menos en teoría y con la información que hay hasta ahora).

Las WebView tienen una pequeña interfaz con unas pocas clases de Java para controlar cosas básicas, y también para que el programa haga algunas de las cosas que no hace la vista por sí misma, por ejemplo, seleccionar ficheros para el <input type=”file”> de HTML. Resulta que no había un API para ello, pero dado que es imprescindible para casi cualquier aplicación HTML5, la gente analizó el código fuente del navegador estándar de Android y encontró la función que se llama cuando se selecciona el campo de subir fichero. La implementé tal cuál, adaptadas a las diferentes versiones de Android:

WbeChromeClient

Pero no funcionaba en KitKat, sólo en las versiones anterior, sin problemas. En KitKat ni se llaman esas funciones, no hace nada cuando se selecciona el campo.

Así fue que busqué, y veo que eliminaron esa interfaz, sin ninguna alternativa. Sólo dicen “no estaba documentada como oficial, y ya pensaremos una alternativa para futuras versiones de Android”. En los comentarios queda claro cómo afecta a tantas aplicaciones, sobre todos a las de PhoneGap, que están basadas completamente en HTML5: con KitKat es imposible subir ficheros de apps programadas en HTML5.

Sumado a eso, el propio Chrome de Google también falla para subir imágenes.

Es decir, miles de apps que dejarán de funcionar en Android KitKat, por decisiones [estúpidas] de eliminar la única interfaz posible para poder subir ficheros desde el “navegador empotrado” para apps, y por fallos increíbles en el navegador por defecto, Chrome.

La pregunta es: ¿están perjudicando así a aplicaciones en HTML5 por simple estupidez? ¿O es una estrategia para que se usen apps completamente nativas? No hay otra explicación, en cualquier caso, vaya ceguera y/o ineptitud enorme de los ingenieros que toman decisiones de desarrollo en Android. Para miles de :facepalm:.

PS: Mientras tanto, si tenéis Android KitKat y queréis subir fotos en Menéame o cualquier otro sitio, tenéis que usar Firefox u Opera Mini. Si habéis desarrollado una app que usa WebViews, estáis jodidos, porque no hay otra alternativa, al menos hasta que salga el GeckoView for Android, pero le falta todavía mucho tiempo :(

Nota: cómo hacer esos vídeos de la pantalla.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 502 seguidores