June 2008 Archives

Hace unos días me llegó mi nuevo ordenador: un Dell Vostro 1510. Intel Core 2 Duo, 2 Gigas de memoria, 1440x990 de video manejado por una nVidia 8400, etc. Los Vostro no son los ordenadores más bonitos del mundo, pero me costó menos de 600 euros, lo que no está nada mal.

Por defecto te instalan Windows Vista pero tengo pocas ganas de aprender a usar otro sistema operativo de Microsoft, así que hice que me instalaran el XP. El disco duro tiene 160 GB y está totalmente ocupado por la partición Windows. Podría redimensionarla e instalar Ubuntu en una segunda partición, pero prefiero no tocar el disco y usar uno externo.

Así que compré un Lacie externo de 2.5 pulgadas y 160 GB, que se conecta por USB 2.0 al portatil y decidí instalar la última versión de Ubuntu, un sistema operativo que conozco bien ya que en el trabajo uso Ubuntu 7.04.

Cualquiera puede hacer algo similar, siempre que la BIOS de su PC soporte arrancar desde una unidad USB. Para ver si esto es así, entra en la configuración de la BIOS y comprueba si la unidad USB está en la lista de orden de arranque (Boot Sequence). En mi portatil, la configuración original arranca desde el disco duro interno, así que lo cambié para que primero probase en el CD/DVD, luego en la unidad USB y por último en el disco duro interno. La idea es que si no se detecta un disco duro externo, el portatil arranque normalmente desde el interno.

Una vez hechos los cambios en la BIOS, inserta el CD de instalación de Ubuntu, conecta el disco duro externo por USB y reinicia el PC. El programa de instalación de Ubuntu arrancará y mostrará las opciones de instalación.

No voy a entrar en los detalles de todas las pantallas de instalación, salvo en las importantes en lo referente a la instalación en el disco USB. La primera de ellas es la de las particiones. Yo escogí Configuración Manual. El programa de instalación considera el disco duro interno como el sda, y el disco duro externo como el sdb. Tienes que crear las particiones para Ubuntu en el sdb. Crea una partición ext3 del tamaño que quieras (la mía es de 60 GB) y móntala en /. A continuación, crea una partición de swap de 2 GB (aunque algunos dicen que el tamaño de la partición de swap ha de ser el doble que el tamaño de la memoria RAM. Tú mismo).

La otra pantalla importante es la última, la que contiene el botón "Install" y el resumen de la instalación. En ella puedes encontrar un botón "Advanced", que te deja configurar donde se instala Grub. Grub es el programa que permite elegir el sistema operativo que se desea arrancar.

Por defecto, el programa de instalación de Ubuntu pretenderá instalar Grub en el disco duro interno, lo que es exactamente lo contrario de lo que queremos conseguir. Cambialo para que instale Grub en sdb, es decir, en el disco USB.

A continación, pulsa Install y deja que se instale Ubuntu en tu disco duro externo. Esto llevará unos minutos.

Una vez haya acabado el programa de instalación, se expulsará el CD de Ubuntu y ya puedes arrancar tu ordenador. No desconectes el disco duro USB.

Una vez arranques, si todo ha ido bien te aparecerá el menú del Grub, lo que te permitirá elegir el sistema operativo con el que trabajar. Por defecto, Ubuntu 8.04 aparecerá como primera opción..

Puedes pulsar Enter para intentar arrancar Ubuntu pero... no funcionará. ¿Por qué?

Si recuerdas, durante el programa de instalación, tuviste que instalar Ubuntu en el disco sdb, es decir, en el disco considerado secundario durante el proceso de instalación. Pero una vez que se arranca el ordenador, el primer disco es el USB (sda) y el interno el segundo (sdb). La configuración de Grub dice que hay que arrancar Ubuntu en el disco 1 (sdb), pero debido a que el primer disco que se arranca es el USB, este es ahora el 0, en vez del 1. Por tanto, hay que editar la configuración de Grub.

Para ello, pulsa e, y luego pulsa otra vez e para editar la primera línea del menú Grub. Aparecerá una línea como la siguiente:
    root(hd1, 0)
    
Cambiala por
    root(hd0,0)
 
Pulsa b (boot) y Ubuntu arrancará.

Ten en cuenta que los cambios que acabamos de hacer en la configuración de Grub son solo temporales, por lo que habrá que cambiar manualmente el fichero de configuración de Grub. Para ello, hay que editar el fichero /boot/grub/menu.lst. Editalo como root y haz exactamente el mismo cambio que hemos descrito arriba.

La siguiente vez que arranques, simplemente pulsa Enter y Ubuntu se iniciará sin problemas.

Hemos conseguido nuestro propósito: instalar Ubuntu en un disco duro externo sin tocar para nada el interno. Esto es especialmente conveniente si tu empresa te da un portatil con XP preinstalado, pero tu quieres trabajar con Ubuntu. Simplemente sigue las instrucciones, y cada vez que quieras trabajar con Ubuntu conecta el disco duro externo a un puerto USB de tu portatil. Cuando desees trabajar con XP, lo más sencillo es dejar el disco duro externo desconectado. XP se inicializará automaticamente.

Empiezo aquí lo que espero sea una larga serie de artículos sobre concurrency en Java 5 y 6. En los primeros artículos hablaré de mecanismos de sincronización.

Uno de los mecanismos de sincronización introducidos en Java 5 es la barrera (Barrier). Las barreras se usan para bloquear un grupo de threads hasta que todos ellos hayan completado su trabajo. Solo entonces se desbloquean todos los threads.

El JDK dispone de la clase CyclicBarrier, que es especialmente adecuada para el caso de algoritmos iterativos paralelizables que pueden descomponerse en un número discreto de subtareas.

Cuando un thread ha acabado su tarea, ha de bloquearse y esperar a que el resto de threads completen las suyas. Para ello, el thread llamará al método await de la clase CyclicBarrier. Cuando el último de los threads haya completado su trabajo, llamará a await, el objeto CyclicBarrier detectará que todos los threads han acabado y los desbloqueará a todos. En ese momento, el objeto CyclicBarrier se reinicializa para que pueda volver a ser utilizado de nuevo.

Cuando se crea el objeto de tipo CyclicBarrier, hay que decirle el número de threads que componen el grupo de threads. Además, es posible pasarle una instancia de la clase Runnable, que se ejecutará cuando todos los threads hayan invocado el método await, justo antes de que se desbloqueen todos los threads.

Por ejemplo, supongamos que tenemos un array de 1000 enteros, y que queremos obtener la suma de todos sus elementos. Asumimos también que disponemos de un sistema con, por ejemplo, 4 procesadores. El algoritmo es facilmente paralelizable: dividimos conceptualmente el array en 4 partes, y asignamos cada una de ellas a un thread. Al final, sumamos los cuatro valores devueltos por los 4 threads. Naturalmente, esto sería demasiado sencillo :), así que para complicarlo un poco introduciremos un requerimiento adicional: queremos que el programa nos dé feedback mientras está trabajando. Para ello, queremos que se impriman por pantalla las sumas parciales cada 100 números. Como tenemos 4 threads, cada thread sumará 25 elementos y esperará a que los otros threads hagan lo mismo. Entonces se sumarán los resultados de cada uno de los 4 threas y el ciclo volverá a empezar.

El código está dividido en tres clases. La clase Adder es la que hace el trabajo de recorrer el array e ir sumando los elementos. Adder implementa Runnable, y es ejecutada por un thread. Los threads se instancia en la clase Main. Por último, una instancia de la clase PartialPrinter es notificada por cada uno de los threads cada vez que se completa una suma parcial de los elementos del array. Además, es la clase que almacena las sumas parciales de los elementos del array.

package concurrency;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Adder implements Runnable
{

  private CyclicBarrier barrier;
  private int[] array;
  private int startPosition;
  private int endPosition;
  private int partial;
  private PartialPrinter partialPrinter;

  public Adder(CyclicBarrier barrier, int[] array, int startPosition,
          int endPosition, int partial, PartialPrinter partialPrinter)
  {
    this.barrier = barrier;
    this.array = array;
    this.startPosition = startPosition;
    this.endPosition = endPosition;
    this.partial = partial;
    this.partialPrinter = partialPrinter;
  }

  public void run()
  {
    int start = this.startPosition;
    int sum = 0;
    int howMany = 0;
    while (start <= this.endPosition) {
      sum += this.array[start];
      howMany++;
      if (howMany == partial) {
        partialPrinter.notifyPartial(sum);
        howMany = 0;
        sum=0;
        try {
          barrier.await();
        }
        catch (InterruptedException ex) {
          Logger.getLogger(Adder.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (BrokenBarrierException ex) {
          Logger.getLogger(Adder.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
      start++;
    }
  }
}

package concurrency;

public class PartialPrinter implements Runnable{
  private int sumOfAll=0;
 
  public synchronized void notifyPartial(int sum)
  {
    this.sumOfAll+=sum;
  }
 
  public void run()
  {
    System.out.println("Sum so far: "+sumOfAll);
  }
}


package concurrency;

import java.util.Random;
import java.util.concurrent.CyclicBarrier;

public class Main
{

  private static int LONG_ARRAY = 1000;
  private static int THREADS = 4;
  private static int BLOCK_SIZE = LONG_ARRAY / THREADS;
  private static int PARTIAL_BLOCK = 25;

  public static void main(String[] args)
  {
    PartialPrinter partialPrinter = new PartialPrinter();
    CyclicBarrier barrier = new CyclicBarrier(4, partialPrinter);

    int[] array = initializaArray(LONG_ARRAY);
    Adder[] adder = new Adder[THREADS];
    for (int i = 0; i < THREADS; i++) {
      adder[i] = new Adder(barrier, array, i * BLOCK_SIZE, (i * BLOCK_SIZE) +
              BLOCK_SIZE - 1, PARTIAL_BLOCK, partialPrinter);
    }
    for (int i = 0; i < THREADS; i++) {
      new Thread(adder[i]).start();
    }
  }

  private static int[] initializaArray(int howMany)
  {
    int[] array = new int[howMany];
    Random random = new Random();
    for (int i = 0; i < howMany; i++) {
      array[i] = random.nextInt(10);
    }
    return array;
  }
}

About this Archive

This page is an archive of entries from June 2008 listed from newest to oldest.

May 2008 is the previous archive.

Find recent content on the main index or look in the archives to find all content.

Categories

Powered by Movable Type 4.1