Subsecciones

- 2.3.1 Soluciones hardware
  - SISD
  - SIMD
  - MIMD
- 2.3.2 Soluciones software

# 2.3 ARQUITECTURAS

En este tema se tratarán tanto las arquitecturas hardware como software que se han desarrollado para conseguir hacer trabajo paralelo o distribuido. Estas arquitecturas fueron inventadas para superar los problemas de rendimiento y responden a distintos enfoques para conseguir sacar más rendimiento gracias al paralelismo. Algunas arquitcturas pretenden tener una mejor relación velocidad/precio y otras ser las máquinas más rápidas del mercado.

Las soluciones hardware fueron las primeras en aparecer, las soluciones software tuvieron que esperar hasta que el hardware diese la potencia necesaria y en el caso de los sistemas distribuidos se tuvo que esperar a que en los años 70 se desarrollaran las redes de área local. En el capítulo Sistemas operativos se explicará con detalle el software distribuido a nivel de sistema operativo.

## 2.3.1 Soluciones hardware

Las soluciones hardware han estado en el mercado de la computación desde sus inicios. Para muchas empresas la única manera de crear mejores máquinas era crear arquitecturas paralelas a partir de las que ya poseían.

Como el número de estos tipos de máquinas es elevado, existen numerosas y diversas soluciones. Quizás la división más conocida y básica sea la taxonomía de Flint. Flint dividió las soluciones hardware en cuatro categorías:

- SISD: un flujo de instrucciones único trabaja sobre un flujo de datos único, a esta categoría pertenecen las CPUs simples y las superescalares.

- SIMD: un flujo de instrucciones único trabaja sobre un flujo de datos múltiple, en esta categoría tenemos los computadores matriciales.

- MISD: un flujo de instrucciones múltiple trabaja sobre un flujo de datos único, resultado teórico de la clasificación, el modelo que más se acerca son los computadores sistólicos.

- MIMD: un flujo de instrucciones múltiple trabaja sobre un flujo de datos múltiple, estos son los multiprocesadores y multicomputadores.

Se irá viendo cómo se explota el paralelismo en el hardware a varios niveles: desde lo más pequeño (procesadores) hasta lo más grande (multicomputadores).

### SISD

Un procesador se dice superescalar cuando acepta varias instrucciones a la vez, por tanto se ejecutan a la vez. Todo microprocesador destinado a computadores actuales es superescalar, para que un microprocesador sea superescalar necesita tener redundancia de unidades funcionales, esto no se debe confundir con los computadores SIMD. Estas CPUs pueden realmente ejecutar `N` instrucciones a la vez (son superescalar es grado `N`) solamente si no hubiera dependencia entre los datos. Hay tres formas en las que podemos ejecutar las varias instrucciones simultáneas:

- Emisión de instrucciones en orden con terminación en orden: según llegan las instrucciones se ejecutan, hay que parar el proceso en cada dependencia de una instrucción con la anterior. También las instrucciones más rápidas tienen que esperar a que terminen las instrucciones más lentas para mantenerse el orden de partida.

- Emisión en orden con terminación en desorden: las instrucciones pueden acabar en cualquier orden, evitando tener que esperar a las instrucciones lentas.

- Emisión en desorden con terminación en desorden: las instrucciones se emiten y acaban en cualquier orden, este es el método que consigue mayor rendimiento. Se necesita tomar decisiones sobre qué instrucción ejecutar primero de entre un buffer de instrucciones. Esta técnica hace tiempo que la vienen usando los compiladores para conseguir mayores optimizaciones de los programas.

El problema de los ordenadores superescalares está en los llamados parones de instrucciones. Los parones de instrucciones se producen por la dependencia que puede existir en instrucciones secuenciales. Estos parones son retardos, vienen dados cuando existe una dependencia entre dos instrucciones que están intentando usar un mismo registro para escribir datos o una instrucción que debe esperar a que otra actualiza un dato para leer de él. Para eliminar los parones2.14 podremos recurrir a:

- Renombramiento de registros, esta técnica también viene siendo usada por compiladores hace tiempo y varias arquitecturas la soportan, por ejemplo los R10000 tienen 64 registros para renombramiento.
- Adelantamiento de datos a nivel de unidades funcionales, hacemos que no tenga que esperarse que el dato se almacene en un lugar determinado para que esté disponible.
- Predicción de saltos, desde las las sencillas hasta las de nivel 2 o las de predicción por trazas del Pentium4.

Usando estas técnicas eficientemente, en ausencia de dependencia de datos y otro tipo de parones, es cuando se consigue el auténtico paralelismo en computadores superescalares. Estas técnicas se suelen dar a dos niveles:

- Nivel de procesador.

  Son los procesadores con planificación dinámica. El procesador lee las instrucciones secuencialmente, decide qué instrucciones pueden ser ejecutadas en un mismo ciclo de reloj y las envía a las unidades funcionales correspondientes para ejecutarse. El problema de esta solución es que carga al procesador de trabajo y el circuito electrónico para implementarlo requiere muchos transistores. Por ejemplo el procesador AMD K7 tiene un planificador con 36 entradas para las operaciones de punto flotante, con esas 36 entradas toma las decisiones oportunas para rellenar un registro de 88 entradas que son la cola que espera para que las 3 unidades de ejecución totalmente independientes del K7 las ejecuten. En este ejemplo se ve la complejidad del hardware necesario.

- Nivel de compilador. La máquina VLIW.

  Un procesador VLIW deja al compilador hacer todo el trabajo de decisión. El compilador VLIW agrupa las instrucciones en paquetes de una cierta longitud y los envía al procesador. Las decisiones se toman una sola vez en tiempo de compilación y no cada vez en tiempo de ejecución. El problema es que al tener que enviar paquetes de instrucciones fijos (y por otros trucos) se necesita enviar instrucciones NOPS2.15 para rellenar los huecos vacíos. Como ejemplo la arquitectura MAJC de Sun dispone de cuatro unidades de ejecución totalmente independientes que no saben nada de los tipos de datos, esto quiere decir que pueden realizar cualquier tipo de operaciones por lo que cuando se ejecuta un programa de un solo tipo de operación (por ejemplo coma flotante) no se están desaprovechando las unidades que no se encargan de ese tipo (enteros). Eso permitirá a este procesador un máximo uso de su cauce.

Un procesador se dice segmentado cuando tiene sus funciones divididas en varias subfunciones que se realizan de forma independiente y que pueden ejecutarse simultáneamente2.16. Si todo el procesador actuara como un bloque, solo se podría ejecutar una sola instrucción a la vez que tardase `M` ciclos, `N` instrucciones tardarían `N`*`M` ciclos. En un ciclo sin parones pueden ejecutarse `N` instrucciones en `M +(N-1)M/k` ciclos siendo `k` el número de etapas. Por tanto cuantas más etapas más trabajo paralelo se realiza y más velocidad se obtiene.

Así por ejemplo un procesador dividido en 4 etapas sin dependencias de datos y empezando por la primera instrucción se ve como en el cuadro .

Tabla: Arquitecturas. Procesador dividido en 4 etapas y sin dependencias

| Tiempo 1 | Tiempo 2 | Tiempo 3 | Tiempo 4 | Tiempo 5 | Tiempo 6
Subfunción 1 | Instr 1 | Instr 2 | Instr 3 | Instr 4 | Instr 5 | Instr 6
Subfunción 2 |  | Instr 1 | Instr 2 | Instr 3 | Instr 4 | Instr 5
Subfunción 3 |  |  | Instr 1 | Instr 2 | Instr 3 | Instr 4
Subfunción 4 |  |  |  | Instr 1 | Instr 2 | Instr 3
Resultados |  |  |  |  | Result 1 | Result 2

Si se ejecutaran a la vez tantas instrucciones como subfunciones tuviera podría realizarse una instrucción por ciclo. Como ejemplo, las 3 unidades de punto flotantes que se han visto en un ejemplo anterior del K7 están divididas en 15 etapas.

Seguidamente se muestran unos datos sobre como esta división en tantas etapas permiten a los K7 hacer las operaciones en menos ciclos por instrucción que a los Pentium4, que están divididos en menos etapas (ver cuadro ).

Tabla: Arquitecturas. Inferioridad del procesador P4 frente a K7

Instrucción | ciclos en P4 | ciclos en K7
FADD | 1 | 1
FMUL | 2 | 1
FDIV float | 23 | 13
FDIV double | 38 | 17
FSQRT float | 23 | 16
FSQRT double | 38 | 24

Para los amantes de las mejores arquitecturas aquí se da una buena razón para inclinar la balanza hacia los de Advanced Micro Devices. La diferencia entre ciclos por instrucción sería mucho mayor al incluir en el cuadro una arquitectura PPC de Apple: éstas llegan a ser competitivas aún funcionando a 800MHz en unos tiempos donde los P4 llegan ya los 3GHz.

La solución mixta de los dos anteriores es un procesador segmentado superescalar. En este procesador entran varias instrucciones a la vez que se ejecutan en cada una de las subfunciones, así se están ejecutando a la vez `M*N` instrucciones siendo `M` y `N` los grados de segmentación y escalabilidad del procesador. Si en el cuadro se añadiese la capacidad de ser superescalar en cada casilla cabrían `N` instrucciones, así donde antes había `instrucción 1` ahora habría `instrucción 1 ... instrucción N`. Asimismo en `instrucción 2` habría `instrucción N+1 ... instrucción 2N`, etc.

### SIMD

Los computadores SIMD son bastante menos frecuentes que los SISD eso se debe a que su programación es bastante más compleja. Un computador SIMD está compuesto de una serie de unidades de ejecución, unos módulos de memoria y una red de interconexión. Gracias a las conexiones cualquier elemento de proceso puede acceder a cualquier módulo de memoria. El paralelismo se consigue ejecutando la misma instrucción sobre varios datos a la vez (cada elemento de proceso un dato). Aquí es donde se complica la programación pues antes de ejecutar la instrucción debe especificar dónde se encuentra los datos en memoria y la configuración de la red de conexión en cada instante.

Aunque se han intentado construir máquinas SIMD puras no han tenido demasiado éxito, no eran suficientemente flexibles y poco eficientes para algunas operaciones (por ejemplo operaciones condicionales). Hoy en día lo que se desarrollan son extensiones vectoriales de procesadores SISD, como ejemplos tenemos AltiVec de Motorola, MMX de Intel o 3DNow de AMD.

Motorola ha apostado por añadir más hardware: 32 registros de 128 bits para operaciones vectoriales. Dos unidades de ejecución independientes (una ALU vectorial y otra para permutaciones), puede mantener una operación por ciclo siempre que encuentre los datos en la cache de nivel 1.

El MMX de Intel en cambio usa registros de 64 bits que ya existían (registros del coprocesador para punto flotante) y añadió 57 instrucciones. Con SSE anteriormente conocida como MMX2 se añadieron 8 registros nuevos de 128 bits y una unidad independiente de suma vectorial. También hay una unidad de coma flotante vectorial pero comparte parte de su pipeline con la unidad de ejecución de coma flotante corriente. Cuando se quiere hacer una operación de 128 bits, se dividen en dos y se envían a las unidades de suma y multiplicación por lo que no siempre se obtiene una instrucción por ciclo, pero permite a Intel mantener sus buses internos del procesador a 64 bits.

3DNow implementa MMX, SSE y SSE2 de la misma manera que Intel y añade sus propias instrucciones para las que no pone registros nuevos (pero tiene registros ocultos al programador, para hacer renombramiento), no permite realizar operaciones de 128 bits pero a cambio las unidades de coma flotante son totalmente independientes.

El problema de estas nuevas instrucciones es el mismo que tuvieron las máquinas SIMD: són más difíciles de programar. Hoy en día se usan mucho los lenguajes de alto nivel por lo tanto se deja la optimización y vectorización de los datos a los compiladores.

Como curiosidad, notar que el compilador de Intel, que es capaz de vectorizar aproximadamente la mitad de los bucles de un programa y que usa SSE2 para tantas operaciones en punto flotante como puede, aumenta los rendimientos por encima de los del procesador AMD K7 en instrucciones como FADD, FMUL y FSQRT. Tras ver el cuadro 2 se comprueba como realmente el AMD K7 es más potente en lo que se refiere a operaciones en coma flotante, no obstante por el momento no dispone de compilador específico en el mercado, esto lo pone en posición desfavorable frente al P4 dentro del uso del paralismo.

### MIMD

Pueden distinguirse multiprocesadores y multicomputadores.

Un sistema multiprocesador consta de muchos procesadores independientes unidos por algún mecanismo entre ellos y la memoria. La primera consideración que se hace al desarrollar un sistema multiprocesador es cómo conectar los procesadores entre sí y éstos a la memoria. Los procesadores deben estar conectados o al menos tener una zona de memoria común, pues están haciendo un trabajo cooperativo.

Según el tipo de acceso a memoria de todos los procesadores de la máquina MIMD se caracterizan en dos tipos, que son: UMA (acceso uniforme a memoria) y NUMA (accesso no uniforme a memoria).

- UMA significa que todos los procesadores acceden a la misma memoria a la misma velocidad, por ejemplo un sistema SMP es UMA, Por ejemplo los compatibles con x86 tienen una arquitectura con la memoria compartida, global para todos los procesadores, con un solo bus para acceder a memoria que comparten todos los procesadores.
- NUMA significa que no los procesadores no pueden acceder a toda la memoria con la misma velocidad. Habrá zonas de memoria a la que se accede más rápido y zonas a las que se accede a menos velocidad. Los procesadores tienen conexión directa con una memoria local, pero también tienen una conexión más lenta a memorias locales de otros procesadores (la memoria local no es memoria cache).

Los sistema UMA a su vez pueden ser de dos tipos dependiendo de la red de interconexión. Así en los multiprocesadores en bus (los procesadores comparten buses de dirección, datos y control) existe un árbitro en este bus que es quien decide quien puede en cada momento acceder a los buses. El problema de esta arquitectura es que los buses se saturan con facilidad, a partir de 64 CPUs el bus es el cuello de botella y aumentar el número de CPUs no aumenta el rendimiento.

Figura: Arquitecturas. Multiprocesadores en bus

Los multiprocesadores con conmutador són la solución para los problemas que conlleva el bus y, evidentemente, es una arquitectura más cara. La memoria se divide en módulos, varios procesadores pueden acceder a memoria de forma simultánea. Las CPUs y las memorias están conectadas a través de puntos de cruce. Si dos procesadores quieren acceder simultáneamente a la misma memoria, tendrán que esperar, en cualquier otro caso el acceso se hace de forma simultánea. El problema que conlleva esta arquitectura es que el número de conmutadores es alto `N`x`M` (si `N` es el número de procesadores y `M` el número de módulos de memoria) además estos conmutadores son caros pues tienen que ser de alta tecnología para que no se haga más lento el acceso a la memoria.

Figura: Arquitecturas. Multiprocesadores en conmutador

Para solucionar estos problemas se creó la red Omega que tiene un número menor de conmutadores. Pero en esta red, y por tener menos elementos de conmutación, ocurren más esperas que en una red de barras como la anterior, aunque menos que en una estructura en bus. Para solucionar todos estos es para lo que se creó la estructura NUMA, pero esta estructura necesita algoritmos complejos.

Figura: Arquitecturas. Red Omega

Un problema que estos sistemas MIMD tienen que solventar es la coherencia de las memorias cache. Las memorias cache de cada procesador guardan los datos que este está accediendo más frecuentemente. En la ejecución normal de un procesador se leen los datos de memoria y se dejan en memoria cache, las próximas lecturas o escrituras se realizan en ella ahorrando tener que ir a memoria principal. El problema ocurre con las escritura en estos datos. Pongamos un ejemplo:

1. la CPU 1 lee de memoria la variable `varGlobal`.
2. la CPU 2 lee de memoria la misma variable.
3. la CPU 1 y la CPU 2 escriben en su copia local de cache un valor.
4. Cuando esos valores se actualizen en memoria (depende de si la cache es de postescritura o escritura directa) estaremos ante una condición de carrera. Además el valor escrito por un procesador no se ha propagado a los demás procesadores que están trabajando con un valor antiguo.

Se podría pensar que la solución sería no permitir memoria compartida o tener un mecanismo para controlar la situación en ese tipo de memoria, no obstante hay que tener en cuenta que también puede ocurrir el caso de que un proceso corriendo en el procesador 1 lea las variables propias (que pueden ser privadas), entonces se mueve al procesador 2 donde escribe en esas variables. Cuando vuelve a migrar al procesador 1 las variables que el proceso piensa que escribió (la migración es transparente al proceso) no están escritas.

Para evitar estas situaciones se han desarrollado una serie de técnicas. La primera de ellas es dividir la memoria principal y dejar parte de esa memoria como memoria local. Sólo se pasarían a cache los datos de esa memoria local y esto plantea bastantes problemas (copiar datos de la memoria global a la local, perdemos eficiencia en accesos a memoria compartida) y seguramente por esta razón no es una solución amplamente usada.

Tener la cache compartida por los procesadores implicaría una velocidad de bus muy elevada, no se implementa en la práctica por sus costes económicos.

Existen las cache privadas con directorio compartido donde se guarda el estado de cada uno de los datos, gracias a esta información se implementa un algoritmo que mantiene la cache coherentes. Se implementa en sistemas con multiprocesadores con redes de conexión (no en bus). Existen varios protocolos que se diferencian en la información que guardan referida a los bloques de memoria y como se mantiene esa información.

- Directorios completos.

  El directorio tiene un bit de procesador que indica si el bloque está o no en la cache del procesador y un bit de modificado si ha sido modificado, si ha sido modificado, sólo está en ese procesador y sólo él tiene permiso para modificarlo. También hay 2 bits por cada bloque de la memoria cache, uno indica si ese bloque es válido o no y el otro indica si se puede escribir en él.

- Directorios limitados.

  Cuando el número de procesadores es muy alto, la solución anterior requiere mucha memoria, para disminuir esta necesidad se usan directorios limitados que siguiendo el mismo esquema, imponen un límite al número de procesadores. Si se solicita el bloque en más cache de las que entradas tiene el directorio se aplica un algoritmo de reemplazo. El directorio debe especificar el procesador porque ahora no sabemos como antes que bits indican que procesadores.

- Directorios encadenados.

  El directorio sólo indica el primer procesador que tiene el bloque, este procesador indica el siguiente creando una cadena, hasta encontrar el procesador que tiene el bit que indica final de cadena activado.

Entre las soluciones más implantadas en multiprocesadores con sistema de memoria basado en bus se encuentran los protocolos de snoopy: los procesadores se mantienen a la escucha de lo que ocurre en los distintos bloques de cache. Ya que el problema ocurre cuando se escribe un dato en la cache, cuando se escribe uno hay dos posibilidades:

- Invalidar el dato de todas las demás cache cuando uno de los procesadores escribe en su cache ese mismo dato. El problema de esta solución es que puede ocurrir que un dato sea muy utilizado y se tenga que estar cogiendo de memoria constantemente pues fue invalidado.

- Actualizar los datos de los demás procesadores cuando modificamos un bloque, el problema de esta aproximación es que se puede generar mucho tráfico y el bus tiene que ser rápido.

A nivel de máquina se han desarrollado otros sistemas para conseguir paralelizar todo tipo de tareas a todos los niveles. El primero de ellos son las IRQs lo que permiten al procesador estar ejecutando programas a la vez que los periféricos están ejecutando sus funciones. Este es un paralelismo a nivel de máquina que permite que se lleve la cuenta del reloj a la vez que se acepta una pulsación de una tecla a la vez que el procesador está haciendo algún proceso pesado.

Tras las IRQs se implantó el DMA, esto es un chip especial dentro de la placa del ordenador que permite ser programado para que un periférico haga accesos a un bloque de memoria sin intervención de la CPU, esto permite mayor autonomía de los periféricos que ejecutan más funciones sin interrumpir a la CPU, con un bus compartido no podrán acceder más de un periférico a la vez a memoria.

Los sistemas multiprocesadores actuales están consiguiendo mejores políticas de gestión por parte del sistema operativo.

Para poner un ejemplo de hacia donde se dirigen las nuevas capacidades de los procesadores para trabajar en paralelo se presenta el caso del procesador MAJC de Sun.

MAJC es capaz de, cuando está en una configuración multiprocesador y detecta que en el thread actual se ha llegado a un bucle del que se tardará en salir, producir de manera especulativa (al igual que se realizan instrucciones) un nuevo thread que se ejecuta en otro procesador ejecutando las posibles instrucciones futuras en tiempo presente, a esto lo llaman STC (computación espacio temporal).

MAJC implementa también vertical multithreading. Así cuando un thread tiene un fallo de cache y tiene que ir a memoria a por el dato se hace un cambio de contexto y se ejecuta otro thread hasta que el dato ha llegado. Esto es posible porque mucho espacio del procesador MAJC se gasta en registros y es capaz de mantener la información de estado necesaria para hacer el cambio de contexto entre dos threads en ellos, con lo que el cambio de contexto es el más rápido que puede implementarse actualmente y da tiempo a ejecutar instrucciones antes de que el dato de memoria haya llegado.

Multicomputadores:

Ya se vió en el tema de introducción cómo aparecieron y evolucionaron estos sistemas. Es una arquitectura MIMD donde cada uno de los procesadores puede tener a su vez varios procesadores. En este apartado lo que antes era un procesador ahora será un ordenador entero. Cada uno tiene acceso directo a su memoria local. Entonces es la misma arquitectura que en multiprocesadores pero escalada. También aquí se puede tener una arquitectura en bus u otro tipo, podrán aplicarse exactamente las mismas explicaciones sobre arquitecturas de las que hablamos en multiprocesadores, solo se varía el esquema levemente:

- Bus: por ejemplo una red de ordenadores conectados a través de una LAN se adapta a este esquema. Las velocidades de transmisión son menores que en un sistema multiprocesador en base a buses y las latencias son mayores. Básicamente igual que su homólogo en multiprocesadores, excepto que esta arquitectura es NUMA (pues un ordenador accede a su memoria local mucho más rápidamente que a la memoria de otro ordenador), se usan algoritmos complejos para las operaciones con memoria.

- Conmutadores: ahora los conmutadores son los propios nodos, pues tienen la capacidad de conectarse a sus nodos vecinos (tienen tarjetas de red) se pueden dar en dos arquitecturas diferentes:
  1. Transputers: los ordenadores forman una retícula 2D. Cada nodo solo tiene que conectar con (como mucho) 4 nodos más en cualquier caso, esto es perfecto para escalar el cluster sin tener que añadir tarjetas de red a los nodos ya existentes. Nótese que no hay simetría pues los nodos en los bordes se conectan con menos nodos.
  2. Hipercubos: los ordenadores forman una retícula 3D. Es difícil de visualizar a partir del orden 4. Cada nodo se conecta a N nodos siendo N el grado del hipercubo. Es una configuración simétrica.

## 2.3.2 Soluciones software

Una vez que el hardware obtuvo la potencia suficiente se empezó a explotar el paralelismo a nivel de software, en el capítulo sobre sistemas operativos se verá cómo se comenzó dividiendo el tiempo de CPU entre varios procesos, esto permitía una mayor eficiencia e interacción entre usuarios y máquinas. Con la llegada de las IRQ el sistema operativo tuvo que gestionarlas, teniendo así la responsabilidad de mantener todos los periféricos trabajando a la vez el máximo tiempo posible.

Los sistemas operativos tienen la responsabilidad de utilizar eficientemente los mecanismos que le da el hardware para hacer trabajos paralelamente. Así por ejemplo en multiprocesadores es el sistema operativo quien tiene que distribuir los procesos por los procesadores de una forma que todos los procesadores estén ocupados. El kernel tiene que tener en cuenta que un proceso se ejecuta más eficientemente en un procesador donde se ejecutó recientemente, pues el procesador tiene en su cache datos de ese procesador, también tiene que tener en cuenta si todos los procesadores tienen acceso a entrada/salida o no. Por ejemplo Windows NT ha elegido una forma muy sencilla que es usar `N-1` procesadores para los `N-1` procesos más prioritarios y el último para el resto, el rendimiento de este esquema es más que discutible, pero desde luego es simple.

La evolución de las soluciones software ha seguido la misma que las soluciones hardware: hasta que el hardware no las soportaba las soluciones software no se hacían realmente eficientes. Así tradicionalmente todos los recursos de una máquina los ha gestionado el núcleo y éste se ha ido adaptando a las nuevas posibilidades, aunque siempre ha habido hardware de un nivel más alto que controlaba su propio funcionamiento, como grandes sistemas gestores de bases de datos.

Con la llegada de los multicomputadores, se plantearon nuevos retos, pero esta vez en vez de ser superados por los diseñadores de sistemas operativos, se vió como un problema de las aplicaciones. Quizás esto fue así porque cuando aparecieron las primeras redes no eran algo barato ni tan común, por eso se consideraba tan puntero el sistema operativo Unix al soportarlas. Tampoco se disponía de hardware barato y potente. Por lo tanto aunque el núcleo daba funcionalidades básicas (creación de procesos, comunicación, manejo de red) todo el problema se dejaba al espacio de usuario. Afortunadamente esta aproximación está cambiando y ya existen varios sistemas de ficheros enfocados a tener un sistema de ficheros único en todo el cluster, pero aún queda mucho por hacer.

Hoy en día una red es muy barata y están muy extendidas por lo tanto las aplicaciones que funcionan sobre ellas se han extendido en todas las áreas del software. Al principio estas aplicaciones eran muy básicas, eran poco más que los protocolos que implementaban (que para la época implementar el protocolo era un logro suficiente), poco a poco se fueron haciendo más complejas. Por aquel entonces Sun hacía mucho desarrollo e implementó algo que hasta la fecha está siendo muy utilizado: RPC. Este sistema permite una comunicación de dos máquinas, una de las máquinas ejecuta unos procedimientos de la otra máquina.