Archivo para la categoría ‘Sistemas Operativos’

Con Eclipse mejor Sun JDK que OpenJDK

Una nota rápida: si usas Eclipse en Linux seguramente este esté usando OpenJDK para ejecutarse. OpenJDK es la implementación libre de la plataforma Java y es generalmente el paquete que viene por defecto en la mayoría de las distribuciones, entre ellas en Ubuntu.

Pero de vez en cuando a Eclipse le da por petarse con un SIGSEGV. Vamos, con una violación de segmento de toda la vida. Y esto es porque el OpenJDK tiene algún error.

Para solucionarlo basta con desinstalar el OpenJDK e instalar la versión oficial de Java que está en los repositorios:

diego@box:~:$ sudo apt-get remove openjdk
[...]
diego@box:~:$ sudo apt-get install sun-java6*
[...]
diego@box:~:$

Con estos simples comandos, aunque perdemos la pureza que algunos quieren darle a los sistemas GNU/Linux de que todo el software sea libre, tu Eclipse estará contento y trabajarás mucho mejor. Porque de lo que se trata es de que trabajes bien y a gusto con tu sistema informático. La filosofía la dejamos para otro día.

Twitter en Haiku con atributos extendidos

Algún día hablaré escribiré largo y tendido sobre el uso de atributos extendidos en los sistemas de archivos, pero, de momento, aquí tenemos una aplicación de Twitter para Haiku que hace uso de ellos. Y no sólo eso, sino que en la captura de pantalla ¡salgo yo!

Haiku Twitter

Bueno, no salgo yo, sale un twit que hice yo sobre Haiku.

Otra aplicación de BeOS/Haiku que usa atributos extendidos es Mail, donde cada correo electrónico se guarda como un archivo y cada campo del mismo (de, para, asunto,…) se guarda como un atributo del archivo y el contenido real del correo es lo único que se guarda en el archivo.

Muy, muy útil. No se como todavía no se ha adoptado de forma masiva en sistemas Linux.

Ubuntu 10.10 Maverick Meerkat

A veces padezco de versionitis, como ahora, por lo que he actualizado mi versión 10.04 de Ubuntu a la versión 10.10 desde el instalador gráfico. Y, cómo no, aquí están los comentarios de rigor:

  • La actualización ha sido perfecta, a pesar de los más de 2 GB que he tenido que descargar (tengo muchos paquetes, sobre todo de desarrollo). Eso sí, ha tardado lo suyo, unas cinco horas entre descarga e instalación de paquetes.
  • La nueva tipografía, “Ubuntu”, aunque se instala, hay que aplicarla a mano desde las preferencias. Pero sí que merece la pena el cambio. Es una fuente sensacional, de muy fácil lectura y de una muy buena apariencia.
  • En general el sistema va más fluido. No es un cambio radical pero sí que se nota en ciertas partes.
  • El menú de sonido que integra Rhythmbox (entre otros) está bien. No es que sea la panacea pero está bien.
  • He aprovechado para instalar Chromium (Google Chrome para Linux) desde su repositorio oficial y me ha dado un error al cargar la librería libmoon. Este librería es la encargada de dar soporte al navegador para Moonlight, la versión de Silverlight de Microsoft para Linux. Para solucionarlo simplemente desinstaladla. De hecho, desinstaladla.
  • Además, también he instalado Cardapio, un sustituto del menú principal de Gnome bastante bueno que se parece al de Windows 7 (sí, he dicho Windows…). Pero lo interesante es que puedes buscar como en Spotlight para lanzar aplicaciones, con lo que casi seguro prescindo de Gnome Do (aunque todavía estoy probándolo).
  • El único problema que he notado es que el calendario y ubicaciones que salen cuando haces clic en el reloj de la barra de tareas sale a la derecha del todo de la pantalla pero a media altura, no pegado a la barra de tareas. Y el caso es que todavía no he visto quejarse a nadie, así que no se si seré yo sólo (aunque me ha pasado en dos equipos).

Resumiendo, la tipografía nueva es sensacional y se agradece en la lectura y el sistema va más fluido en general, así que aunque esta versión no es LTS (tres años de soporte), sí que merece la pena el cambio, así que, ya sabéis ;) .

La forma “fácil” de compilar el kernel de Linux en Ubuntu

Como frikis geeks de la informática que somos, si usamos cualquier sabor de GNU/Linux, puede que algún día nos entre el gusanillo de compilar nuestro propio kernel.

La forma menos fácil es bajarse los fuentes con git, configurarlo con make menuconfig, compilarlo con make y luego copiar los archivos donde corresponda. Una búsqueda en Google y nos dará todos los tutoriales que queramos.

Pero los usuarios de Debian y derivados, entre ellos Ubuntu, tenemos una forma mucho más sencilla de hacerlo:

  1. Instalamos los paquetes necesarios que incluyen el código fuente del kernel y las herramientas para compilarlo:

    $ sudo apt-get install build-essential kernel-package linux-source libncurses5-dev

  2. Descomprimir el código fuente que acabamos de instalar:

    $ cd /usr/src
    $ tar -xjf linux-source-version.tar.bz2

  3. Configurar nuestras propias opciones del kernel:

    $ cd linux-sources-version
    $ make menuconfig

  4. Crear un paquete Debian (*.deb) con el nuevo kernel (esto tardará un poco porque tiene que compilarlo). El comando make-kpkg debe ejecutarse como root, de ahí el fakeroot, aunque también se puede ejecutar con sudo con un poco de cuidado:

    $ fakeroot make-kpkg --initrd --append-to-version=nuestra-versión kernel-image kernel-headers

  5. Instalar nuestro nuevo kernel:

    $ cd ..
    $ sudo dpkg -i nombre-de-nuestro-kernel.deb

  6. Si todo ha ido bien, tendremos nuestro kernel instalado con su correspondiente entrada en el menú de GRUB. En caso de que no esté en GRUB, se debe ejecutar el comando update-grub.

Además de con los fuentes de los repositorios, también se puede hacer con los fuentes que se bajen de git teniendo en cuenta que el parámetro --append-to-version es necesario (sino puede que de el error package xxx not in control info).

Un truco: para acelerar un poco la compilación, se puede indicar a make-kpkg que lance varios procesos en paralelo. Para ello hay que exportar la variable CONCURRENCY_LEVEL con el número de procesos máximo a utilizar.

$ export CONCURRENCY_LEVEL=4

Hay que tener en cuenta que esta variable no debería ser superior al número de procesadores/núcleos del ordenador porque caería el rendimiento.

Luego, por supuesto, hay que reiniciar el sistema y elegir en la pantalla de GRUB nuestro nuevo kernel mirando que no hayamos metido la pata al configurarlo.

Y esto es todo. Si hay algún error (que lo habrá) lo ponéis en comentarios y también si os ha funcionado, si habéis obtenido mejor (o peor :P ) rendimiento, si no funcionaba…

Mi personalizado comando less

El comando less es una utilidad de los sistemas UNIX (cualquier sistema operativo compatible con POSIX lo debería tener) que sirve para ver los contenidos de los archivos de forma completa, pudiendo navegar entre dichos contenidos y consumiendo muy pocos recursos.

El comando less puede personalizar su salida, es decir, se puede incluir información adicional del archivo que se está viendo en la visualización del mismo, como el nombre del archivo, el tamaño, en qué posición nos encontramos, etc.

Por defecto la información mostrada es escasa, por lo que una pequeña personalización nunca viene mal. A continuación os dejo la que yo tengo en mi sistema:

alias L='less -NR -P "?f%f:<stdin>.?m (%i/%m). [%s bytes] %lt/%L (%pB \%) ?x-> ?x%x"'

Más o menos muestra el número de cada línea, el nombre del archivo abierto o <stdin> en caso de que sea la entrada estándar, la página en la que estás, el tamaño, el porcentage en el que te encuentras y el siguiente archivo pasado a less (en caso de que haya).

Así, con less tienes más información de lo que estás viendo. Por supuesto esto es totalmente personalizable, así que un poco de man less no viene mal para saber como mejorarlo ;) .

En este caso, Linux es mejor que Windows

Es curioso que vaya a ver el vídeo del nuevo iPhone 4G desde Chrome en Windows y me diga, sin sorprenderme, que necesito QuickTime para poder reproducirlo. Y el famoso QuickTime viene con iTunes de regalo.

Lo divertido de esto es que desde Chrome en Linux basta con hacer clic en el mismo para verlo insertado en el navegador o, si no estás conforme (yo no lo estaba porque no tenía controles de pausa, avanzar ni retroceder), basta con hacer clic con el botón derecho y darle a la opción “Ver en el reproductor”.

Por supuesto, hay que tener los extra codecs, pero instalarlos es inmensamente menos doloroso que bajarse el QuickTime, instalarlo y encontrarse con que iTunes ahora controla toda tu música.

En este caso (y no es aislado), Linux gana a Windows ;) .

Actualización a Ubuntu 10.04 Lucid Lynx

Imagen de inicio de Ubuntu 10.04

Hoy me he actualizado a Ubuntu 10.04 desde la versión anterior, la 9.10, mediante el actualizador automático, y me he encontrado con algún que otro problemilla:

  • Ha tardado casi cuatro horas. Esto no es un problema realmente, pero entre las descargas y la instalación con dpkg se ha tirado un buen rato. Bien es cierto que tengo unos cuantos paquetes de desarrollo como gcc, g++, apache, mysql,…
  • Después de instalar, en el reinicio, no se me veían los bordes de las ventanas, con lo que no las podía mover, ni cerrar, ni nada. Eso era porque al darle a instalar tenía activado Compiz, y en el reinicio se desactivó. Para solucionarlo, basta con ir al Menú principal > Sistema > Preferencias > Apariencia y ahí, en la solapa Efectos visuales, seleccionar cualquiera que no sea el básico (luego ya se configurará al gusto) para activar de nuevo Compiz.
  • En otro ordenador que también instalé, tuve el problema de que en el reinicio no cargó correctamente el tema Ambiance ya que yo tenía un tema personalizado. Basta con seleccionar de nuevo el tema a aplicar en Menú principal > Sistema > Preferencias > Apariencia y listo.

Por cierto que el cambio de los marrones a los morados y grises me parece muy acertado; excepto, por supuesto, el cambio de los botones de las ventanas de la derecha a la izquierda (y estuve un rato probándolos, pero no me apaño). Para cambiarlos, en la aplicación gconf-editor se va a la ruta /apps/metacity/general/button_layout y se cambia el texto close,minimize,maximize: por el texto menu:minimize,maximize,close.

A parte de esto, ningún problema. Eso sí, eso de los 10 segundos en arrancar todavía les queda un poco lejos. Cierto es que arranca rápido, más rápido que la versión anterior, pero que no se echen tantos faroles. Esperemos que la próxima versión sea la vencida.

The Ubuntu Manual

Ubuntu Manual Project

Acaba de salir The Ubuntu Manual, un proyecto que intenta crear un manual para el sistema operativo Ubuntu, una distribución de GNU/Linux.

En este manual se encuentra la información que un usuario final necesita para hacerse con el sistema, desde el principio, es decir, desde la instalación en un equipo hasta la configuración del mismo pasando por la información necesaria para el manejo de las aplicaciones que usamos en el día a día, como un navegador Web, un cliente de correo electrónico, etc. Además, por supuesto, de mantenimiento del sistema e instalación de software adicional.

Pero lo mejor, quizás, es que está en español (y en unos cuantos idiomas más), capturas de pantalla incluidas. El problema es que todavía no está en español :( .

A nivel de usuario, creo que es lo que faltaba para que la adopción de otros sistemas operativos empiece a despuntar. Bien es cierto que con Windows tampoco teníamos manual, digamos que aprendimos “de oídas”, así que ahora que tenemos oportunidad, creo que es buena idea ponerse con esto y empezar, no quizás a eliminar por completo la otra alternativa, pero sí a probar para ver, al menos, qué es lo que nos estamos perdiendo.

Si vuestro uso del ordenador es el de un usuario normal, es decir, más o menos, navegar por Internet, leer el correo electrónico, chatear, editar documentos de texto y hojas de cálculo, ver presentaciones, escuchar música y ver vídeos1, os animo a todos a probar Ubuntu, que tiene manual, o cualquier otra distribución de GNU/Linux.

1 Por supuesto se pueden hacer muchas más cosas que estas, esto sólo es el principio.

Creando pthreads en Linux (y sus problemas)

Hoy toca otra vez de esos tostones informáticos que no interesan a nadie, como el de GCD, pero que lo pongo para hacer copia de seguridad de estas pequeñas investigaciones.

Estoy haciendo un proyecto donde tengo que utilizar multiproceso para que varias tareas se ejecuten simultáneamente (nunca mejor dicho) que consiste en conectar a varios equipos lanzando varios hilos para que el intento de conexión a uno no haga esperar las conexiones a los demás. Una vez lanzados todos se espera por ellos hasta que terminen de conectar para iniciar el resto del proceso.

El desarrollo es en Linux y en C++ y todo el proceso está dentro de una sola aplicación, es decir, no hay varias aplicaciones que se lanzan desde una shell de forma simultánea, no, es una única aplicación desde donde está todo el proceso, por eso no se pueden usar varios procesos sino que se usan hilos, exactamente pthreads.

Los pthreads son el estándar POSIX para hilos de ejecución. Además, también definen la sincronización de los mismos mediante mutex y variables de condición (que, por cierto, nunca he entendido su funcionamiento; yo uso semáforos).

Para facilitar la mantenibilidad del software, he implementado toda la funcionalidad mediante orientación a objetos, incluidos los hilos, semáforos, mutex, etc., por lo que para iniciar un hilo, se haría de la siguiente forma:

class MyThread : public Thread {
public:
MyThread() : Thread {}
virtual ~MyThread() {}
virtual void* run(void* arg) {
cout << “MyThread::run()” << endl;
}
};

MyThread* thread = new MyThread();

thread->start();
thread->wait();

delete thread;

Dentro de la función start() se crea un pthread y se inicia mediante la función pthread_create(...). En la función wait() se espera por el hilo hasta que finalice su ejecución mediante la función pthread_join(...).

La clase Thread cuenta también con funciones para parar un hilo, reiniciarlo, etc. También se puede pasar como parámetro la función a ejecutar o, incluso, una clase que herede de la clase ThreadFunction que tiene la función run() para que así esta implementación sea bastante polivalente.

Hasta aquí todo bien. La verdad es que funciona de forma bastante estable, creo que es sencillo de utilizar (al menos es lo que pretendo) y bastante mantenible ya que cualquier modificación afecta sólo a la clase y apenas hay que tocar el resto de código.

Pero, a partir de aquí, en cuanto se empieza a hacer un uso intensivo, es cuando comienzan los pequeños problemas.

Resulta que la función pthread_create() no sólo crea un nuevo pthread sino que también lo inicia, es decir, no hay ninguna función que sea pthread_start(), sino que para iniciar (y parar) un hilo hay que crearlo (y destruirlo). Además, los desarrolladores de los pthreads no están por la labor de hacer estas funciones.

El problema surge cuando se crean muchos hilos e inmediatamente después se espera por ellos, por ejemplo, de la siguiente forma:

#define limit 100

MyThread* threads[limit];
for(int i = 0; i < limit; i++) {
threads[i] = new MyThread();
threads[i]->start();
threads[i]->wait();
}

// no hay que olvidarse de destruirlos

El problema está en hay veces que se crea un hilo dando como resultado un pthread_t válido (el identificador del hilo), pero no da tiempo a iniciarlo antes de que se ejecute el pthread_join(), por lo tanto se está haciendo una espera sobre un hilo creado pero no iniciado. En teoría pthread_join() debería devolver un valor del tipo EINVAL o similar pero no. En la práctica lo que se produce es un fallo de segmentación, es decir, corrupción de memoria.

Para reproducir esta funcionalidad, he hecho un pequeño programa en C++ que, ejecutado varias veces, produce este comportamiento incorrecto. En este caso no he conseguido corrupción de memoria, pero tampoco es está completo.

La solución que se me ocurre es usar un semáforo que sincronice la creación y la iniciación del hilo con la espera del mismo. No es una solución demasiado elegante pero creo que funcionará.

La verdad es que el API de POSIX respecto a hilos no me parece demasiado buena, pero creo que no tengo nada que hacer. Es en estos momentos es cuando hecho de menos el API de hilos de BeOS/Haiku ;) .

Actualización 25/03/2001: En esta entrada, extrañamente, es donde más spam entra (aproximadamente un 90 % de todo el que recibe el blog) así que he cerrado los comentarios.

ASUS Eee PC y Windows Starter

El otro día se compró mi hermano un ASUS Eee PC 1001 con Windows 7 Starter (en una tienda muy conocida y al precio standard).

El equipo en sí está muy bien. Es pequeño, no hace ruido, apenas se calienta, con una pantalla brillante y nítida y suficientemente grande como para navegar perfectamente por Internet, leer el correo electrónico, ver vídeos, leer libros,… por supuesto, todo para uso personal. En el momento que aparece otra cabeza para ver la pantalla ya se está demasiado cerca… aunque a veces hasta es mejor :P .

Pero el sistema operativo, Windows 7 Starter… bueno, eso mejor insertáis una unidad USB, arrancáis y lo formateáis completamente. Porque Windows 7 Starter da verdadero asco.

Que sí, que vale, que está hecho para dispositivos con poca potencia, pero no, no es normal que no se pueda cambiar el fondo de pantalla. Sí, habéis leído bien: en Windows 7 Starter no se puede cambiar el fondo de pantalla.

Y, a parte de esto, tampoco tiene la interfaz Aero (cosa casi lógica, si quieres), ni hay previsualizaciones de las ventanas en la barra de tareas, no reproduce DVD (ni con un DVD externo), no se puede cambiar entre usuarios sin cerrar sesión y ni siquiera tiene el modo de compatibilidad con Windows XP. Vamos, completito, completito. Y no, no es por las capacidades del hardware. Es pura política.

De verdad, hay veces que confías y dices “vaya, por fin han hecho un producto que funciona” y ahora van y la cagan con esta mierda.

Hibernando el sistema en Linux (y solucionando algún que otro problema)

La hibernación es una característica de los sistemas operativos que permite que toda la memoria RAM se copie al disco duro de forma que, en el próximo reinicio del sistema, se cargue la memoria RAM del archivo previamente guardado sin necesidad de realizar el inicio del sistema de forma normal.

Con esto se consigue que no sea necesario cerrar ninguna sesión ni ningún programa que esté en ella y, la próxima vez que se arranque el ordenador, todas las aplicaciones estarán en el mismo estado que cuando lo hibernamos.

En Linux disponemos de esta opción desde el menú de apagado de la interfaz gráfica, por lo que basta hacer clic en el botón correspondiente para que nuestro sistema hiberne.

Pero, además de esta opción, en los sistemas Linux también tenemos un comando que realiza esta acción: hibernate.

Con este comando conseguimos hibernar el sistema desde una consola o desde un script de apagado automático de la máquina, para que en el próximo reinicio tengamos todas nuestras aplicaciones abiertas.

Pero la función hibernate no está exenta de sus riesgos. A mi los errores que me daba eran principalmente dos:

El primero es que si había varias hibernaciones seguidas, esto es, según termina de iniciar el sistema después de una hibernación realizar otra (mediante el comando), en el segundo reinicio después de esta hibernación, se producían errores en las aplicaciones debido a fallos de segmentación en alguna de las librerías que usaba (las que vi fueron libc.so y libdbus.so).

Me inclino a pensar que después del reinicio no sabía exactamente donde estaban localizadas las librerías en memoria y por eso fallaba. O, quizás el kernel de Linux tenga una función de posicionamiento aleatorio del código para evitar ataques y todavía no funciona todo lo bien que debería. Pero, como digo, esto son suposiciones. Además, después de un poco de uso normal, en las siguientes hibernaciones todo iba perfectamente.

El segundo de los problemas se producía con GRUB 2, el nuevo gestor de arranque que traen las distribuciones de Linux más modernas (en mi caso Ubuntu 9.10). GRUB 2 tiene un registro llamado recordfail que se fija a uno cuando el sistema arranca y a cero cuando se apaga correctamente. Si se apaga incorrectamente y recordfail queda a uno, en el siguiente reinicio no aparece el timeout del menú de inicio del sistema por lo que hay que seleccionar el sistema operativo a arrancar manualmente.

Con el comando hibernate, una vez que inicia de nuevo el sistema, no fija de nuevo la variable recordfail de GRUB 2 a uno, con lo que en los siguientes reinicios después de hibernar, siempre hay que seleccionar el sistema a arrancar de forma manual.

Para solucionar esto, hay que configurar el comando hibernate. El archivo de configuración, como es norma en sistemas Linux, está en /etc/hibernate/hibernate.conf. En este archivo hay que añadir las siguientes líneas:

LockGnomeScreenSaver true
OnResume 00 /etc/pm/sleep.d/10_grub-common thaw

La primera línea bloquea la sesión de Gnome con su salvapantallas (porque tampoco lo hace por defecto con lo que después del reinicio aparecía la sesión sin contraseña; un gran fallo de seguridad) y la segunda línea indica que se ejecute el archivo /etc/pm/sleep.d/10_grub-common con el parámetro thaw, que es el archivo que fija recordfail a cero en cada inicio correcto del sistema.

Con esta pequeña solución, ya tenemos el comando hibernate listo para incluir en nuestro script de apagado automático sin que tengamos que arrancar todas las aplicaciones en cada reinicio.

Formateando

Disco Duro

Ayer me compré un disco duro externo por USB de 1,5 TB para las copias de seguridad y demás, pero lo compré por separado (caja más disco) y, lógicamente, lo tuve que formatear en casa.

Y el caso es que para esos 1,5 TB, el Windos XP Home que venía en el portátil tardó, nada más y nada menos, que ¡¡5 horas!! en formatear en NTFS. Lo cual me inclina a pensar que si llego a comprarme uno de 3 TB, hubiera tardado 10 horas ¿no? Estupendo este NTFS.

Ahora no lo voy a probar (5 horas son demasiado para repetirlo) pero me gustaría ver cuánto hubiera tardado con ext4 o, por probar, con btrfs. Con BFS, por ejemplo, hubiera tardado menos de un minuto (o poco más) ya que hace poco más que escribir la información del disco y el mapa de bits de espacio libre.

Pero es lo que tenemos que aguantar si queremos que funcione tanto en Windows como en Linux (y sí, ya se que hay soluciones para que desde Windows podamos leer ext2 y ext3, pero no me fío demasiado).

Comunicación entre procesos

A la hora de desarrollar un sistema operativo, después de haber planificado la gestión de memoria y la gestión de procesos e hilos, le toca el turno a la comunicación entre los distintos procesos, IPC en inglés, imprescindible para el funcionamiento del sistema.

Para la comunicación a bajo nivel (en el kernel) entre hilos existen varias técnicas que se pueden resumir en:

  • Memoria compartida: Varios procesos, aunque en espacios de memoria distintos, comparten una zona de la misma donde leen y escriben los datos a compartir. La ventaja es que se pueden compartir grandes cantidades de datos y es un sistema muy rápido y sin problemas para lecturas. En cambio, existe el problema de la sincronización del acceso cuando varios procesos tienen que escribir en la misma zona de memoria simultáneamente.
  • Paso de mensajes: Es un sistema donde, mediante canales compartidos entre los procesos, éstos leen y escriben para comunicarse. Se suele implementar mediante sockets, pipas y FIFO‘s aunque hay más tipos de implementación. La ventaja es que las lecturas y escrituras son síncronas por lo que no hay problemas de lectura y escritura simultáneas. El problema es que la cantidad de datos a comunicar puede estar limitada.

El sistema operativo BeOS y su sucesor, Haiku, han implementado una solución muy elegante: por un lado, como los sistemas compatibles con POSIX tiene la compartición de memoria; por otro lado, implementó dos técnicas adicionales:

  • El paso de mensajes entre hilos directamente mediante las funciones send_data(...) y receive_data(...) con el inconveniente de que las funciones son síncronas y después de enviar el primer mensaje, hasta que no se lee no se puede enviar otro.
  • El paso de mensajes mediante puertos. Los puertos son el homólogo a los sockets pero entre procesos y mucho más rápidos. Existe un elemento común entre dos hilos llamado “puerto” a través del cual los procesos se comunican mediante las funciones write_port(...) y read_port(...). La ventaja de esto es que el puerto puede tener un buffer por lo que, hasta que no se llene, las llamadas a estas funciones son asíncronas.

Con estas dos técnicas, se consigue tanto la comunicación entre procesos como la sincronización entre distintos hilos gracias a que las llamadas a las funciones de lectura y escritura son síncronas.

Debido a que este tipo de comunicación, aunque muy rápida, es bastante complicada de aplicar, sobre todo porque los datos pasados entre los distintos procesos no tienen tipo, son flujos de bytes, se han implementado otras técnicas de comunicación, pero estas a nivel de aplicación, es decir, no es el proceso el que responde, sino la aplicación completa mediante una API dedicada a ello.

Algunos ejemplos de estas implementaciones son RPC, MPI, COM, DCOM, DDE, OLE, CORBA, ICE, D-Bus, DCOP, MBUS, RMI, Sockets, Doors, OpenBinder, etc.

En esta lista hay que destacar OpenBinder. OpenBinder es una implementación de un nuevo tipo de comunicación entre procesos. Este proyecto nació con lo que iba a ser la siguiente versión de BeOS, la 6, aunque posteriormente maduró dentro de Palm al ser esta empresa la que compró los derechos sobre BeOS.

OpenBinder está un nivel por encima del resto de implementaciones de comunicación entre procesos. Y digo un nivel por encima porque esta API proporciona una comunicación, por decirlo de alguna forma, a nivel de componentes, es decir, los componentes del sistema operativo, incluyendo los componentes de la interfaz gráfica (ventas, botones, etc.) son clases que heredan de clases de OpenBinder, con lo que cualquier componente es susceptible de comunicarse con cualquier otro. Este sistema inicialmente implica bastante complejidad, pero está optimizado para ser muy rápido y, además, lo que se pretende con él es poder hacer aplicaciones, especialmente GUI‘s como las X, que sean distribuidas de forma transparente para el desarrollador.

OpenBinder no merecería más atención que la del resto de sistemas si no fuese porque esta tecnología está implementada dentro del sistema operativo Android, dejando de lado el resto de sistemas. Esto es cuanto menos curioso, ya que el resto de tecnologías están ampliamente probadas y esta, quizá, sea la más nueva. Pero aquí es donde se ve lo que gasta Google en I+D; no se conforma con lo que hay, quiere lo mejor (según ellos, claro).

Y ya puestos, se pueden empezar a hacer conjeturas: por ejemplo, los dispositivos Android completos podrían estar en la nube de Google, no sólo los datos que manejan, por lo que tu teléfono móvil o tu ordenador podrá ayudar a Google a procesar sus propios datos.

Ahora hay que esperar un poco más a que salga Chorme OS para ver qué sistemas de comunicación tiene y saber si esta teoría será real o una simple paranoia mía :) .

Analizando GCD

Hace unos meses que Apple sacó a la venta la última versión de su sistema operativo, Mac OS X 10.6 Snow Leopard.

Esta versión es principalmente una versión de, como dicen ellos, afinamiento. No se han incorporado características nuevas sino que se han refinado las actuales dándole al sistema una mayor velocidad y estabilidad. Lo que sí han hecho es incorporar en el núcleo del sistema una tecnología que no se ve pero que sí se nota: Grand Central Dispatch.

Logotipo de Grand Central Dispatch

GCD es una nueva tecnología que lo que hace es distribuir las tareas que tienen que hacer las aplicaciones en tareas más pequeñas para así aprovechar toda la potencia de las máquinas que tienen varios procesadores o varios núcleos por procesador, como viene siendo habitual en los ordenadores de hoy en día, no sólo los Apple.

Esta tecnología viene a solucionar el problema de la escasez de paralelismo con la que contaba Mac OS X, pero, más que por su incapacidad, por la desidia de los desarrolladores a incorporarlo en sus aplicaciones. Aunque tienen sus razones, claro, es bastante complicado sincronizar hilos cuando no tienes un API que te ayude a hacerlo.

GCD añade dos cosas al nuevo sistema operativo de Apple. La primera es lo que se conoce como un pool de hilos, es decir, en lugar de que cada vez que queramos realizar una acción de forma paralela creando un hilo para así aprovechar los diferentes procesadores, lo que hacemos es indicar al sistema que queremos realizar tareas paralelas. Es el propio sistema el encargado de crear los hilos correspondientes y de ejecutarlos con una de las tareas a realizar.

La diferencia con los hilos tradicionales es que los hilos de GCD ni se crean ni se destruyen (esto me suena), simplemente existe una o varias listas de estos hilos (el pool) donde cada tarea se va a asociando a cada uno de estos hilos hasta que termine. Una vez terminada la tarea, el hilo no se destruye, sino que vuelve a la lista correspondiente. Además, estas listas de hilos tienen diferentes prioridades con lo que se puede tener más precisión a la hora de ejecutar tareas.

Lo bueno que tiene esto es que el programador no tiene que preocuparse de nada más que de indicar cuáles son las tareas a realizar de forma paralela. El sistema se encargará de distribuirlas entre las distintas listas de hilos disponibles.

Pero para lograr esto, Apple se ha tenido que sacar de la manga una extensión para el lenguaje C de su compilador Clang. Esta extensión es lo que se conoce en otros lenguajes, como Java o Javascript, como closures. Las closures son funciones anónimas o funciones lambda, algo así como los punteros a funciones de C pero con capacidades extras, como el acceso a variables locales, que se puedan devolver por otra función o que se puedan declarar en línea.

A esto, los desarrolladores de Apple le llamaron bloques. Entonces un bloque sería un trozo de código ejecutable que se parece a una función pero que no tiene nombre (función anónima) y que puede acceder a las variables locales del ámbito donde se ha declarado. Y la sintaxis que han hecho es al mejor estilo de C: austera. Veamos unos ejemplos, aunque la forma de trabajar es similar a Java y Javascript pero con distinta sintaxis:

// bloque asignado a una variable y accediendo
// a una variable local
int b = 3
multiplicar = ^ int (int a) { return a * b; };

// x valdría 6
int x = multiplicar(2);

// declaración de un tipo de bloque
typedef void ( ^ my_block_type)(int count);

// función que repite la ejecución de un bloque n veces
void repeat(int times,my_block_type block) {
for(int i = 0; i < times; i++) {
block(i);
}
}

// declaración en línea (pasando como parámetro
// un bloque completo sin declararlo previamente
// como se hace con los punteros a funciones)
repeat(10, ^ (int count) {
printf(“count = %d\n”,count);
});

Gracias a esta nueva extensión, con apenas trabajo por parte del desarrollador, se puede aprovechar toda la potencia de las máquinas multiprocesador. Y, realmente, cuando se dice con “pocas líneas” es cierto. Basta con identificar las tareas (ese es el trabajo difícil) y usar las funciones dispatch_* de la nueva API pasando como parámetro un bloque de código con la tarea a ejecutar. Es el sistema, de forma transparente, el que se encarga de distribuir las tareas en los distintos procesadores creando los hilos necesarios para ello o utilizando los que están en el pool.

La verdad es que hay que agradecer a Apple que por fin se pusiese las pilas en cuanto al rendimiento de sus sistema, que ya iba bien de por sí, pero siempre puede ir mejor, sobre todo por facilitar el paralelismo de tareas a los programadores. Y aunque esto está muy bien, todavía no he visto por ningún sitio cómo han solucionado el problema de la sincronización/comunicación entre tareas (a parte de la memoria compartida y semáforos, claro), ya que esta extensión es sólo para aprovechar las máquinas multinúcleo.

Y es que, a mi entender, creo que esta solución que tan bien les está yendo, es un parche para un problema que viene de lejos. Venga, va, aquí los abucheos por criticar el Mac OS X. Pero expongo mis razones:

Esta solución crea un pool de threads con los que, a partir de un momento dado, se empiezan a realizar tareas sin la necesidad de estar creando y destruyendo hilos continuamente sino reaprovechándolos. Existe más de un pool con diferentes prioridades para así gestionar mejor las tareas. Esta solución es así porque la forma en Apple que implementó los hilos en Mac OS X no es la de lightweight threads (hilos ligeros) sino la de hilos más parecidos a procesos que a hilos en sí.

Esta solución implica que la creación de cada hilo sea bastante costosa, aproximadamente unos 512 KB por cada uno, mientras que la solución de hilos ligeros, la que implementan BeOS y Haiku (sí, ha salido BeOS, ¿raro en este blog? :) ) es la de verdaderos hilos ligeros, con lo que la creación de los mismos apenas lleva 50 KB (32 KB de memoria de pila y el resto de estructuras internas del kernel).

512 KB por hilo creado es mucha memoria utilizada. Y más teniendo en cuenta la cantidad de aplicaciones y servicios que se están ejecutando en un sistema operativo actual según se inicia. Por pocos hilos que crees estás consumiendo mucha memoria y hay que tener en cuenta que cuantos más hilos (ojo, con un límite), más paralelismo y mayor aprovechamiento del hardware. Y vale que ahora la memoria es barata, pero ¿los nuevos sistemas funcionarán en hardware antiguo? Quizás esto no sea una prioridad para Apple, pero siempre hay que pensar en todo.

Como comenté antes, tampoco sé exactamente como se sincronizan las tareas en Mac OS X, mientras que en BeOS/Haiku tenemos tres mecanismos muy ligeros para ello: semáforos, comunicación entre hilos y puertos. El problema es la complejidad a la que se enfrenta el programador para hacer esta sincronización, aparentemente solucionada en Mac OS X, pero que no debería ser un problema si existe una buena API dentro del sistema que lo facilite.

En conclusión (y ya para terminar este ladrillo de entrada) creo que Apple ha mejorado mucho su sistema con esta característica, tanto para los usuarios, aprovechando el hardware al máximo, como para los programadores, haciendo que con escasas líneas de código aprovechen mejor dicho hardware, pero sigo pensando que la solución inicial de hilos pesados no es tan buena como la solución de hilos ligeros.

¿Para cuándo se podrán grabar las llamadas en Android?

Hay una aplicación que hecho mucho de menos en el sistema operativo Android, y es la aplicación que grabe las conversaciones.

Quizás no la hayan hecho porque violaría la privacidad de dichas conversaciones, pero hay que reconocer que puede llegar a ser muy útil. Siempre que se avise de la grabación se puede usar en un juicio (esperemos que, por nuestro bien, no llegue a ser necesario), pero si no lo vas a usar y es para uso personal, en teoría no habría que avisar de ello ¿no? Pero ¿sería legal?

De todas formas, la parte que me interesa es la tecnológica. Creo que en el API de Android no es posible interceptar una llamada más lejos de saber quién es el que llama, por lo que la grabación de la misma, al menos de momento, no es posible. De todas formas, sí que tiene una clase y una constante, MediaRecorder.AudioSource.VOICE_CALL, para grabar voz (indica que se graba la voz de entrada y de salida, esto es, la del que llama y la del que es llamado), de donde se puede intuir que si se podrá en un futuro.

Además, esto sería extremadamente útil para hacerte tu propio contestador automático dentro del propio teléfono sin contar con los servicios de contestador de las operadoras. Y, por supuesto, es software libre, podemos ir mucho más allá con esta característica. Algo así como una mini centralita Asterisk con todas las posibilidades que ello conlleva.

Y ya que estamos interceptando llamadas, también sería muy buena idea sacar otra aplicación que, entre dos terminales con el mismo software, se pudiesen cifrar dichas conversaciones. De hecho sería buena idea ya que hay quién avisa de que tengamos cuidado con lo que se habla por el teléfono.

Supongo que tarde o temprano lo harán. Es, como dicen en algunos foros, un must have, una aplicación imprescindible. Ahora esperemos que sea más pronto que tarde.

Ya tengo mi Android 1.6

Desde esta mañana, a eso de las nueve y media, ya tengo en mi HTC Magic la actualización de Android a la versión 1.6 (ellos la llaman Donut) a través de las actualizaciones automáticas vía telefónica (en inglés es OTA; siempre poniendo siglas…).

La actualización no ha durado más de cinco minutos reiniciando dos veces y todo funcionando sin problemas.

Ahora a disfrutarla1 hasta que llegue la 2.0, también conocida como Eclair, a finales de año (esperemos ;) ).

Actualización: De momento, a falta de investigar un poco, tengo un fallo con esta nueva versión y es que no se reciben automáticamente los correos electrónicos que llegan a la cuenta de Gmail. Para recibirlos hay que realizar una sincronización manual. He revisado la configuración de sincronización y está todo en automático. He reiniciado y tampoco. Los mensajes de Gmail con Android 1.6 no se reciben en tiempo real. Si alguien sabe algo que comente por aquí.

Actualización 2: Como dije antes, no actualiza en tiempo real. La última prueba que he hecho me ha tardado 12 minutos desde que envié el correo hasta que lo he recibido en el terminal.

Actualización 3 (22/10/2009): Parece que los problemas no son del terminal ni de la actualización sino que son de la funcionalidad de Push email de Vodafone o de Google. Supongo que retrasarían el envío de correos para mejorar la avalancha de descargas de Donut. Ahora parece que todo funciona.

1 Venga, va, algunos se preguntarán «¿cómo se puede disfrutar una versión de software?». Bueno, a los que somos Ingenieros Informáticos por vocación nos pasan estas cosas. Es lo mismo que sientas pasión por cualquier otra cosa, como los coches, la ropa, las antigüedades,… la única diferencia es que lo nuestro no es tangible.