1.1. Servidores de ficheros en red
Los sistemas de ficheros en red permiten acceder a ficheros y directorios remotos de la misma forma que se accedería a ficheros locales, estos sistemas son llamados Sistemas de ficheros distribuidos[1]. Generalmente estos ficheros son organizados dentro de una estructura de ficheros, lo que llamamos Espacio de Nombres.
En la figura 1-1 vemos cómo recursos existentes bajo el punto P en la máquina A pueden ser accedidos por la máquina B. La máquina A tiene los recursos de forma local, estos recursos están distribuidos y son accesibles mediante la red. La máquina B tiene los recursos en remoto, pero accesibles desde su Espacio de Nombres, creando la abstracción de que son locales.
figura
1-1 Ejemplo de jerarquía de ficheros en red
Entendemos como espacio de nombres a un conjunto de nombres que forman una estructura. Cada fichero podrá tener uno o varios nombres que lo identifiquen, en cambio un nombre sólo podrá identificar a un fichero únicamente.
Los nombres, de un espacio de nombres, pueden representar ficheros locales de un sistema de ficheros, ficheros virtuales que puede generar el sistema operativo (con algún propósito), ficheros ubicados en máquinas remotas, etc. Generalmente el espacio de nombres suele tener una estructura tipo árbol.
Puede pensarse que cualquier medio que nos permita acceder a un fichero en una máquina remota es un sistema de ficheros de red. Pero no es así, entendemos un sistema de ficheros en red cuando los recursos distribuidos entran a formar parte del espacio de nombres que tenemos en el cliente.
El uso de un espacio de nombres permite que, tanto los ficheros locales como los remotos, sean tratados por igual. El sistema operativo se encarga de crear una capa de abstracción, traduciendo operaciones realizadas sobre ficheros en llamadas a procedimientos remotos o RPC (Remote Procedure Call)[2]. Esta capa de abstracción suele estar integrada en el núcleo del sistema, las aplicaciones de usuario no llegan a ser conscientes de que acceden a máquinas remotas, el sistema operativo se encarga de todo.
1.1.2. Ejemplos reales de Sistemas de ficheros de red
El protocolo de sistemas de red más difundido es el “Network File System“(NFS) de Sun Microsystems, implementado para numerosas plataformas basadas en UNIX. Tiene una total transparencia para las aplicaciones de usuario puesto que suele estar implementado a nivel de Kernel haciendo uso de las RPC.
El protocolo 9P también es un protocolo de ficheros en red, parecido a NFS, que se usa en el sistema operativo PLAN 9. Se basa también en llamadas a procedimientos remotos. Ambos protocolos guardan muchas semejanzas, como su estructura cliente-servidor o el uso de RPCs.
La diferencia fundamental entre NFS y 9P es que los mensajes de NFS son auto contenidos, cada petición de un cliente contiene toda la información que el servidor necesita para realizar la acción requerida. En una petición de 9P la información que necesita el servidor no está toda especificada dentro de la propia petición, ha podido ser indicada con anterioridad, existe un recuerdo de las acciones anteriores.
Por tanto, NFS es un protocolo que no guarda estado, al contrario de 9P, que realiza sus operaciones dentro de sesiones donde el cliente y el servidor mantienen cierta información común. El hecho de que NFS no guarde estado le hace robusto a fallos en la comunicación, pero al tener que indicar en cada mensaje más información hace que sea más pesado. El protocolo 9P es más ligero en sus transmisiones que NFS.
Plan 9 es un sistema operativo de libre distribución desarrollado por Bell Labs. Se inició a finales de los años ochenta por Ken Thompson, Rob Pike, Dave Presotto y Phil Winterbottom. En su página oficial podremos encontrar toda la documentación necesaria sobre su diseño e implementación.[3]
El desarrollo ha continuado hasta hoy día por múltiples programadores. La versión actual del sistema es la cuarta edición, distribuida bajo la licencia Lucent Public License versión 1.02.[4]
El objetivo que tiene Plan 9 es solventar los típicos problemas de los sistemas clásicos de una forma simple y clara. Los sistemas basados en Unix se han ido adaptando a las ideas nuevas. Originalmente, lo normal era que una sola máquina centralizaba todos los procesos que competían por el tiempo de ejecución y/o recursos. Las nuevas tendencias tienden a un procesamiento distribuido, abaratando costes y obteniendo mejor rendimiento. Ya sea porque se tienen muchos procesadores en la misma máquina o porque se emplean múltiples máquinas, tenemos el problema de coordinar los recursos. Esto ha provocado muchas complicaciones a la hora de adaptar los sistemas clásicos.
Para lograr esta transparencia PLAN 9 tiene tres axiomas:
· Todos los recursos son accesibles desde una estructura jerárquica, como si de ficheros se tratasen, ya sea un recurso de memoria, capacidad de cálculo o cualquier tipo de dispositivo, estará representado en esta jerarquía. Todo es tratado como un fichero.
· Existe un protocolo estándar llamado 9P para acceder a los recursos. Plan 9 quiere ofrecer facilidad a la hora de coordinar y dar acceso a los recursos disponibles en una red. Mediante el uso de 9P los procesos tienen acceso a los recursos.
· Existen múltiples vistas de la jerarquía de los recursos. Cada proceso tiene una visión privada y personalizada de los recursos del sistema, esto se llama Espacio de Nombres de un proceso.
Un ejemplo de cómo podría ser un sistema distribuido construido de esta forma sería el mostrado en la figura 1-2, donde vemos múltiples recursos distribuidos interconectados por conexiones de alto rendimiento. Las máquinas A, B, C y D hacen uso de los recursos. Podría darse el caso de que mientras A ejecuta procesos complejos de cálculo, B podría acceder a un gran volumen de información, ambas máquinas tendrían un espacio de nombres diferente según sus intereses, pero todas acceden a los recursos mediante el protocolo 9P.
figura 1-2 Recursos distribuidos en un sistema Plan 9
Plan 9 tiene mecanismos para personalizar la vista de los recursos públicos, de manera que parezcan cercanos y locales.
Como ya hemos dicho, 9P es un protocolo de ficheros de red. Se basa en llamadas a procedimientos remotos. Cada operación es una transacción cliente-servidor y utiliza la estructura básica donde el cliente realiza una petición y es respondido por el servidor.
Podemos encontrar todos los detalles de la implementación de este protocolo en el Capítulo 5 del manual de Plan 9.[5]
Existen distintos tipos de mensajes para cada acción que desee realizar el cliente 9P. Cuando el cliente envíe un mensaje (T-mensaje) el servidor responderá con el mensaje de respuesta correspondiente (R-mensaje). La figura 1-3 muestra este comportamiento.

figura 1-3 Mensajes 9P entre el Cliente y el Servidor
Tanto el cliente 9P como el servidor 9P deben guardar estado de las acciones realizadas. Cuando un cliente se conecta a un servidor se establece un canal de comunicación bi-direccional, al que llamaremos sesión 9P.
Dentro de una sesión el cliente y el servidor guardan unos identificadores de ficheros comunes entre ellos, también conocidos como FIDS (File Identifier Descriptors). Estos identificadores o FIDS son “punteros” a ficheros del servidor. El cliente hace uso de un FID para indicar sobre qué fichero quiere ejecutar una determinada acción. Esto puede verse en la figura 1-4.

figura 1-4 Uso de descriptores FID entre el cliente y el servidor de ficheros 9P
Cada fichero tiene un identificador único dentro del sistema, que no puede repetirse, denominado QID. Dos QID son iguales si, y sólo si, identifican al mismo fichero en el sistema de ficheros. Pueden suceder que un fichero tenga múltiples FIDs haciéndole referencia, pero sólo tendrá un QID. Algunos R-Mensajes retornan el QID de un fichero.
Aunque el protocolo 9P esté diseñado para su uso en conexiones de red, también se usa dentro de Plan 9 de forma local. Mediante este mecanismo Plan 9 normaliza el acceso a los ficheros, sin hacer distinciones entre los ficheros locales y remotos.
A la hora de usar 9P mediante una conexión de red, los mensajes 9P usan protocolos de transporte. Originalmente 9P usaba el protocolo de transporte IL[6], implementado en PLAN 9, pero lo más usual es usar el protocolo TCP[7] por lo extendido que está.
El protocolo 9P tiene diferentes tipos de mensajes para cada operación a realizar. Podemos dividir los mensajes en dos grupos, los mensajes generados por el cliente (mensajes de petición T-mensajes) y los mensajes generados por el servidor como respuestas (mensajes de respuesta R-mensajes). A continuación vemos una tabla con todos los mensajes de petición y su correspondiente respuesta.
|
Petición (cliente) |
Respuesta (servidor) |
|
Tversion |
Rversion |
|
Tauth |
Rauth |
|
Tattach |
Rattach |
|
Twalk |
Rwalk |
|
Topen |
Ropen |
|
Tcreate |
Rcreate |
|
Tread |
Rread |
|
Twrite |
Rwrite |
|
Tclunk |
Rclunk |
|
Tflush |
Rflush |
|
Tremove |
Rremove |
|
Tstat |
Rstat |
|
Twstat |
Rwstat |
Además de los mensajes anteriores, existe un tipo de mensaje que sólo puede ser generado por el servidor como réplica a cualquier T-mensaje del cliente, éste mensaje es Rerror, útil para indicar al cliente un error en la petición requerida.
La estructura común de un mensaje 9P es la siguiente:
|
size[4] |
TIPO_MENSAJE |
tag[2] |
LISTA_DE_CAMPOS |
El campo size indica el tamaño completo del mensaje.
Todos los mensajes de 9P tienen un identificador de mensaje o tag, utilizado para relacionar las peticiones con su respuesta correspondiente y evitar ambigüedades a la hora de interpretar una respuesta del servidor.
El valor TIPO_MENSAJE especifica el tipo de mensaje. Dependiendo del tipo que sea, el elemento LISTA_DE_CAMPOS contendrá unos parámetros u otros, según la naturaleza del mensaje.
A nivel de transporte, un mensaje 9P es una secuencia de Bytes. Por ello es importante que se conozca el tamaño del mensaje y de cada uno de los elementos que lo componen. Como ya hemos dicho, el parámetro size describe el tamaño del mensaje completo, como podemos ver en la estructura, size está indicado como “size[4]”, lo que indica que el propio dato ocupa 4 bytes. También podemos ver que el valor tag ocupa 2 bytes.
A continuación vemos una descripción de cada tipo de mensaje, de su función y de cómo está formado.
La estructura del un mensaje Rerror es la siguiente:
|
Rerror: |
size[4] |
Rerror |
tag[2] |
ename[s] |
El mensaje Rerror se usa por el servidor para notificar una anomalía a la hora de interpretar o ejecutar otro mensaje. Se envía con una descripción del problema.
Los clientes no pueden enviar mensajes de error al servidor.
Mensajes “version"
A continuación vemos la estructura interna de Tversion y de Rversion.
|
Tversion: |
size[4] |
Tversion |
tag[2] |
msize[4] |
version[s] |
|
Rversion: |
size[4] |
Rversion |
tag[2] |
msize[4] |
version[s] |
El mensaje Tversion es el primero que debe enviar el cliente una vez establecido el canal de comunicación. Su función es negociar algunos aspectos de la nueva conexión. Con este mensaje se indica qué versión del protocolo se está usando y qué tamaño máximo pueden tener los mensajes. El servidor debe responder con el mensaje de respuesta correspondiente Rversion.
El cliente indicará la versión del protocolo que desea usar, respondiéndole el servidor con la misma versión, o una anterior en el caso de no poder usar la solicitada por el cliente. Actualmente sólo existe una versión de 9P activa, pero el protocolo está diseñado para mantener la compatibilidad en un futuro.
Respecto al tamaño de los mensajes, es posible que el medio de transmisión limite este parámetro. El cliente indicará el tamaño máximo que desea, respondiéndole el servidor con el mismo tamaño o uno menor.
En el caso de que no se entienda el mensaje Tversion, o que la negociación sea inaceptable para el servidor, éste responderá con un Rerror, indicando los motivos del error.
Mensajes “auth” y “attach”
La estructura de los mensajes Tattach y Rattach es la siguiente:
|
Tattach: |
size[4] |
Tattach |
tag[2] |
fid[4] |
afid[4] |
uname[s] |
aname[s] |
|
Rattach: |
size[4] |
Rattach |
tag[2] |
qid[13] |
La estructura de los mensajes Tauth y Rauth es la siguiente:
|
Tauth: |
size[4] |
Tauth |
tag[2] |
afid[4] |
uname[s] |
aname[s] |
|
Rauth: |
size[4] |
Tauth |
tag[2] |
qid[13] |
Los mensajes Tattach tienen por objetivo pedir al servidor un acceso al sistema de ficheros. Para ello proponen un FID como raíz en el servidor y el servidor les retornará un identificador único de fichero, también llamado QID, en un mensaje Rattach.
Tauth es un mensaje que propone un FID al servidor para poder autentificarse a través de dicho FID. El servidor responderá mediante Rattach. El FID propuesto por el cliente se empleará para el proceso de autentificación, que queda delegado a un protocolo de autentificación que no forma parte de 9P. En el caso de que el servidor no requiera autentificación responderá con un mensaje Rerror.
Ambos mensajes, Tattach y Tauth, contienen el nombre de usuario uname y un nombre que indica el árbol de ficheros al que se desea acceder aname.
Mensajes “clunk”
La estructura de los mensajes clunk es la siguiente:
|
Tclunk: |
size[4] |
Tclunk |
tag[2] |
fid[4] |
|
Rclunk: |
size[4] |
Rclunk |
tag[2] |
El mensaje Tclunk pide al servidor que elimine el FID indicado del conjunto de FIDS actualmente activos en la conexión. El servidor retornará Rclunk si fue posible realizar la operación, o Rerror en caso contrario. Cabe destacar que esto no eliminará el fichero del servidor, sólo elimina una referencia al mismo.
Los FIDs pueden ser reutilizados una vez eliminados, excepto en el caso de que se retorne Rerror, el FID no podrá ser usado más.
Mensajes “flush”
La estructura de los mensajes flush es la siguiente:
|
Tflush: |
size[4] |
Tflush |
tag[2] |
oldtag[2] |
|
Rflush: |
size[4] |
Rflush |
tag[2] |
El cliente puede quedarse esperando una respuesta del servidor que, por algún motivo, éste no logra enviar. Puede que una lectura se bloquease esperando datos. Para solventar estas situaciones de bloqueo, el cliente puede enviar al servidor un mensaje de tipo Tflush, que forzará al servidor a cancelar la petición referida por el mensaje. Si todo fue correcto el servidor lo comunicará con un Rflush.
Dentro del mensaje Tflush encontraremos el tag del mensaje que tiene que ser cancelado oldtag.
Mensajes “open” y “create”
A continuación vemos la estructura de los mensajes Topen y Ropen.
|
Topen: |
size[4] |
Topen |
tag[2] |
fid[4] |
mode[1] |
|
Ropen: |
size[4] |
Ropen |
tag[2] |
qid[13] |
iounit[4] |
La estructura de los mensajes Tcreate y Rcreate es la siguiente.
|
Tcreate: |
size[4] |
Tcreate |
tag[2] |
fid[4] |
name[s] |
perm[4] |
mode[1] |
|
Rcreate: |
size[4] |
Rcreate |
tag[2] |
qid[13] |
iounit[4] |
Estos mensajes permiten al cliente crear y/o abrir un fichero.
Topen indica al servidor que se abra el fichero indicado por el FID. El servidor retornará un Ropen con el QID del fichero abierto. La apertura se efectuará en el modo indicado por el cliente a través del parámetro mode.
Con Tcreate el cliente pide al servidor que cree un archivo con el nombre name, con los permisos indicados por el parámetro perm. El nuevo fichero será creado dentro del fichero apuntado por el FID indicado por el cliente, por lo que será necesario que el FID apunte a un directorio. Si la creación fue correcta, el servidor retornará el QID del nuevo fichero. Además de crear un nuevo fichero también se efectuará una apertura sobre él, por tanto, es necesario especificar el modo de apertura con el parámetro mode.
Cabe destacar que la operación Tcreate implica también la modificación del FID propuesto por el cliente, que de ser el directorio destino, pasa a ser el nuevo archivo creado.
El servidor hace uso del parámetro iounit para indicar al cliente cuál es número máximo de bytes que garantiza que pueden ser leídos o escritos, de forma que el cliente se adaptará a la hora de escribir o leer información.
Mensajes “read” y “write”
La estructura de los mensajes Tread y Rread es:
|
Tread: |
size[4] |
Tread |
tag[2] |
fid[4] |
offset[8] |
count[4] |
|
Rread: |
size[4] |
Rread |
tag[2] |
count[4] |
data[count] |
La estructura de los mensajes Twrite y Rwrite es:
|
Twrite: |
size[4] |
Twrite |
tag[2] |
fid[4] |
offset[8] |
count[4] |
data[count] |
|
Rwrite: |
size[4] |
Rwrite |
tag[2] |
count[4] |
Estos dos mensajes son el mecanismo de lectura y escritura en los ficheros remotos. Con ellos el cliente podrá acceder a la información y modificarla. Ambas operaciones se efectúan siempre en un FID, que debe esta abierto anteriormente en el modo adecuado (no se podrá escribir un fichero abierto para lectura).
El mensaje Tread es el encargado de leer de los ficheros remotos. Si la operación se ha podido realizar sin problemas, se retornará un conjunto de datos en un mensaje Rread. Con Twrite el cliente pide al servidor que escriba unos datos en un fichero, si todo fue correcto el servidor retornará un mensaje Rwrite.
Ambos mensajes de petición tienen dos parámetros que permiten indicar a partir de que byte se realizará la operación, el valor offset, y qué número de bytes se van a leer o escribir, el valor count. El mensaje Twrite tiene otro campo que contiene el bloque de información que pretende escribirse, el valor data.
Los mensajes de respuesta del servidor tienen un parámetro que indica el número de bytes que han sido leídos o escritos, el valor count. Para el caso de las lecturas, Rread retornará también la información requerida por el cliente en el parámetro data.
Como en todos los mensajes, puede suceder que el servidor no comprenda la petición o esta no pueda ser realizada. El servidor responderá con un mensaje Rerror a este tipo de peticiones, indicando la descripción del error.
Mensajes “remove”
La estructura de los mensajes Tremove y Rremove es:
|
Tremove: |
size[4] |
Tremove |
tag[2] |
fid[4] |
|
Rremove: |
size[4] |
Rremove |
tag[2] |
El mensaje Tremove pide al servidor que elimine un fichero, para ello, el cliente indica el FID que referencia al fichero que se desea eliminar. Si la operación se completó correctamente, el servidor lo indicará con un Rremove, de lo contrario, el servidor retornará un Rerror.
Mensajes “stat” y “wstat”
Los campos característicos de los mensajes Tstat y Rstat:
|
Tstat: |
size[4] |
Tstat |
tag[2] |
fid[4] |
|
Rstat: |
size[4] |
Rstat |
tag[2] |
stat[n] |
La estructura de los mensajes Twstat y Rwstat es:
|
Twstat: |
size[4] |
Twstat |
tag[2] |
fid[4] |
stat[n] |
|
Rwstat: |
size[4] |
Rwstat |
tag[2] |
Estos mensajes están relacionados con los metadatos de los ficheros. Los ficheros tienen una serie de información almacenada sobre si mismos, como puede ser el tamaño o la fecha del último acceso, los campos de la estructura STAT[8] representan estos metadatos.
El mensaje Tstat pide al servidor que retorne los metadatos de un fichero indicado por un FID. El servidor retornará Rstat con una estructura de tipo stat, que contiene variada información acerca del FID requerido.
Cuando le cliente quiere editar los metadatos de un fichero escribe los nuevos metadatos mediante el mensaje Twstat, que envía al servidor una estructura stat y un fid que debe ser actualizado. En el caso de que sea posible efectuar el cambio de los metadatos, el servidor retornará un mensaje Rwstat.
La estructura STAT se compone de los siguientes campos de datos:
|
size[2] |
Tamaño total en bytes de la estructura STAT |
|
type[2] |
Campo para uso interno del Sistema Operativo. |
|
dev[4] |
Campo para uso interno del Sistema Operativo. |
|
qid.type[1] |
Vector de bits correspondiente al modo de apertura del fichero. |
|
qid.vers[4] |
Versión del fichero. |
|
qid.path[8] |
Identificador único del fichero dentro del sistema de ficheros. |
|
mode[4] |
Permisos y modos. |
|
atime[4] |
Marca de tiempo del último acceso. |
|
mtime[4] |
Marca de tiempo de la última modificación. |
|
length[8] |
Tamaño del fichero en bytes. |
|
name[ s ] |
Nombre del fichero. |
|
uid[ s ] |
Nombre del usuario propietario. |
|
gid[ s ] |
Nombre del grupo al que pertenece el fichero. |
|
muid[ s ] |
Nombre del usuario que ha modificado, por última vez, el fichero. |
Al igual que los mensajes, la estructura STAT es una secuencia de bytes con los datos colocados de forma consecutiva.
Mensajes “walk”
A continuación vemos la estructura de los mensajes Twalk y Rwalk.
|
Twalk: |
size[4] |
Twalk |
tag[2] |
fid[4] |
nfid[4] |
nwname[2] |
nwname*(wname[s]) |
|
Rwalk: |
size[4] |
Rwalk |
tag[2] |
nwqid[2] |
nwqid*(qid[13]) |
El mensaje Twalk pide al servidor que asigne un fichero a un nuevo FID. El cliente parte de un FID origen e indica la ubicación del fichero destino a partir de ese FID. El servidor retornará los QIDs de todos los ficheros que recorrió para llegar al lugar indicado (si el fichero destino existe).
El FID que el cliente ofrece como inicio de la ruta no es modificado. El servidor usará un nuevo FID, parámetro newfid, para apuntar al fichero destino de la ruta.
El camino a recorrer se indica por una lista de nombres contenida en wname. Es muy habitual que el cliente quiera duplicar un FID, de forma que envía una petición Twalk con una lista de nombres vacía, y el servidor asume que el fichero donde apunta el FID origen es el mismo que el fichero destino.
1.3.2. Ejemplo de sesión 9P
Supongamos que tenemos un servidor 9P sirviendo la estructura de ficheros indicada en la figura 1-5. En este ejemplo, un cliente se conecta a nuestro servidor para realizar la siguiente acción “leer el contenido de un fichero y escribirlo en otro fichero nuevo”.

figura 1-5 Estructura de ficheros que sirve el Servidor 9P
A continuación veremos paso a paso las acciones que realiza el cliente y las respuestas por parte del servidor. Estas acciones están reflejadas en la figura 1-6, donde vemos cada mensaje de petición y la respuesta.
FASE 1: Establecimiento de la sesión 9P.
1. El cliente inicia la conexión indicando el tipo de versión de 9P y el tamaño de los mensajes. La respuesta del servidor confirma la versión y el tamaño.
2. El cliente trata de establecer un canal para autentificarse. El servidor indica que no está permitido autentificarse.
3. El cliente ofrece un FID para atarlo con la raíz del sistema de ficheros, retornando el servidor el QID del fichero raíz.
Tras estos mensajes, el cliente ha establecido una conexión con el servidor y obteniendo un FID (287) que identifica la raíz del sistema de ficheros.
FASE 2: Apertura del fichero “/dir3/file1”
4. El cliente ofrece un FID para atarlo al fichero “/dir3/file1”.
5. Apertura del fichero “/dir3/file1” para lectura.
El cliente abre el fichero origen en el modo lectura. Después de estos mensajes se mantienen dos FID, el 287 que identifica a la raíz y el 289 que apunta al fichero “/dir3/file1”.
FASE 3: Creación del fichero “/dir1/copia1”
6. El cliente ofrece un FID para atarlo al fichero (directorio) “dir1”.
7. Se verifica la existencia del fichero “/dir1/copia1” atándolo a un FID. El servidor responde con un Rerror, el fichero no existe.
8. Con esta acción el cliente crea una copia de un FID. El FID 291 y el 292 están asignados al mismo fichero físico.
9. El cliente pide al servidor que se cree el fichero “/dir1/copia1” y le asigna el FID 292.
10. El cliente deshecha el FID 291.
Tras estos mensajes, el cliente ha logrado crear el fichero destino “/dir1/copia1”. Se sostienen los FID 287 y 289 anteriores junto con uno nuevo, el 292 que representa el nuevo fichero creado.
FASE 4: Lectura y Escritura.
11. Lectura del fichero “/dir3/file1”. El servidor retorna con un bloque de datos indicando que ha leído 22 bytes.
12. Escritura de 22 bytes al fichero “/dir1/copia1”.
13. El cliente trata de leer más datos del fichero “/dir3/file1”, el servidor responde con un mensaje sin datos. La lectura se da por finalizada.
Se realiza la transferencia de información de un fichero a otro. Todos los descriptores anteriores se mantienen activos.
FASE 5: Cierre de los ficheros
14. El cliente deshecha el FID 289.
15. El cliente deshecha el FID 292.
Se cierran los FIDs, que quedan liberados paro otros futuros usos.
La siguiente figura muestra las peticiones del cliente, columna de la izquierda, y las respuestas correspondientes por parte del servidor, columna de la derecha. La comunicación 9P corresponde al ejemplo anterior.

figura 1-6 Ejemplo de conversación 9P
1.4. JAVA
Es necesario plantearse qué lenguaje es más conveniente para implementar el servidor 9P. El lenguaje seleccionado debe permitir cumplir los requisitos que hemos impuesto para la aplicación, no nos valdría de nada usar un lenguaje que sólo exista en una arquitectura si lo que pretendemos es tener portabilidad.
La elección del lenguaje ha sido JAVA[9]. Su capacidad de portabilidad está llevada al extremo de funcionar sin cambios en cualquier plataforma, que tenga una máquina virtual de java implementada y tenga las librerías que se utilicen.
Java es un lenguaje orientado a objetos con una sintaxis inspirada en C. Incorpora conceptos como sincronización y gestión de tareas dentro del lenguaje, así como toda una colección de clases ya implementadas para ofrecer un entorno de desarrollo completo.
Java basa su filosofía en una máquina virtual (Virtual Machine o VM). La implementación de una aplicación es igual para dos sistemas diferentes, ya que la implementación está hecha para la VM. El código fuente de Java es compilado para la VM, que lo interpreta (En la figura 1-7).

figura 1-7 Programa de JAVA interpretado por la VM.
Existen varias versiones de JAVA, las más interesantes para nuestra aplicación son J2SE (Edición estándar) y J2ME (Edición reducida). La edición estándar de Java se usa en PCs, estaciones de trabajo o incluso servidores, está diseñada para máquinas sin muchas limitaciones en lo que a recursos se refiere. La edición reducida de Java nos ofrece la posibilidad de ejecutar programas en Java en arquitecturas con recursos reducidos, como teléfonos móviles o agendas. La implementación del servidor deberá funcionar en las dos versiones J2SE y J2ME.
El gran problema de Java es su rendimiento. Un programa hecho en Java debe ser interpretado por la VM, que a su vez lo ejecuta en el sistema real. Si usamos un lenguaje de programación como C, el programa estará compilado para la arquitectura del sistema, obteniendo mejor rendimiento. En Java el código será interpretado, no obstante, el rendimiento parece aceptable para nuestro propósito.