La organización de las redes en Plan 9

Dave Presotto

Phil Winterbottom

presotto,philw@plan9.bell-labs.com

ABSTRACT

En un sistema distribuido las redes son de importancia capital. Este documento describe la implementación, filosofía de diseño y organización del soporte de red en Plan 9. Hace referencia a los requerimientos de red para los sistemas distribuidos, nuestra implementación del nucleo, los nombres de red, los interfaces de usuario y el rendimiento. También observamos que gran parte de esta organización es importante en los sistemas actuales.

1. Introducción

Plan 9 [Pike90] es un sistema portable distribuido, multi usuario, de propósito general implementado en una gran variedad de ordenadores y redes. Lo que lo diferencia es su organización. El objetivo de esta organización fué reducir la administración y promover la compartición de recursos. Una de las claves de su éxito como sistema distribuido es la organización y gestión de sus redes.

Un sistema Plan 9 comprende servidores de ficheros, servidores de CPU y terminales. Los servidores de ficheros y de CPU son normalmente máquinas multiprocesador centrales con amplia memoria y conectados con redes de alta velocidad. Una variedad de máquinas tipo estación de trabajo sirven como terminales de los servidores centrales usando varias redes y protocolos. La arquitectura del sistema exige una jerarquía de velocidades de red acordes con las necesidades de sus componentes. La conexión entre servidores de ficheros y servidores de CPU suele consistir en enlaces punto a punto de fibra óptica de banda ancha. Las conexiones entre servidores y terminales suelen ser de velocidad media, como Ethernet [Met80] y Datakit [Fra80]. Conexiones de baja velocidad por Internet y el backbone de AT&T sirve para usuarios de Oregon e Illinois. Lineas básicas de datos RDSI y lineas serie a 9600 baudios proporcionan acceso a los usuarios en casa.

Puesto que los servidores de CPU y los terminales usan el mismo nucleo, los usuario pueden elegir ejecutar programas localmente en sus terminales o remotamente en los servidores de CPU. La organización de Plan 9 oculta los detalles de la conectividad del sistema permitiendo tanto a usuarios como a administradores configurar su entorno para que sea tan distribuido o centralizado como ellos deseen. Mediante comandos simples se puede construir un espacio de nombres local que abarque muchas máquinas y redes. En el trabajo, los usuarios tienden a usar sus terminales como estaciones de trabajo, ejecutando programas interactivos localmente y reservando los servidores de CPU para datos o trabajos de computación intensiva, como compilar programas o ejecutar programas de ajedrez. En casa, cuando se conectan a través de redes lentas, tienden a realizar más trabajo en los servidores de CPU para minimizar el tráfico por la linea. El objetivo de la organización de red es proporcionar el mismo entorno al usuario estén donde estén los recursos que utilice.

2. Soporte de red del nucleo

Las redes juegan un papel importante en un sistema distribuido. Esto es particularmente cierto en Plan 9, donde la mayoría de los recursos son proporcionados por servidores externos al nucleo. La importancia del código de red dentro del nucleo se refleja en su tamaño: de sus 25.000 lineas de código, 12.500 son relativas a red y a protocolos. Constantemente se están agregando redes y la fracción de código de comunicaciones está creciendo. Peor aún, el código de red es complejo. Las implementaciones de protocolos consisten casi por entero en sincronización y manejo dinámico de memoria, areas que demandan sutiles estrategias de recuperación de errores. El nucleo actual soporta Datakit, enlaces de fibra óptica punto a punto, una suite de protocolos de Internet (IP) y un servicio de datos RDSI. La variedad de redes y máquinas ha hecho surgir cuestiones no planteadas por otros sistemas que se ejecutan en hardware comercial y que solamente soportan Ethernet o FDDI.

2.1. El protocolo de sistema de ficheros

Una idea central de Plan 9 es la representación de un recurso como un sistema de ficheros jerárquico. Cada proceso ensambla una vista del sistema construyendo un espacio de nombres [Needham] que conecta sus recursos. Los sistemas de ficheros no necesitan representar ficheros en disco; de hecho, la mayoría de los sistemas de ficheros de Plan 9 no tienen almacenamiento permanente. Un sistema de ficheros típico representa dinámicamente algún recurso como un conjunto de conexiones de red o la tabla de procesos. La comunicación entre el nucleo, los drivers de dispositivos y los servidores de ficheros remotos o locales usa un protocolo llamado 9P. Dicho protocolo consiste en 17 tipos de mensajes, que describen operaciones sobre ficheros y directorios. Los dispositivos residentes del nucleoy los drivers de protocolos usan una versión procedural del protocolo, mientras que los servidores de ficheros externos usan una forma RPC. Casi todo el tráfico entre sistemas Plan 9 consiste en mensajes 9P. Este protocolo se vasa en varias propiedades del protoloco de transporte subyacente. Asume que los mensajes llegan de manera fiable, y en correcta secuencia, y que se preservan los delimitadores entre mensajes. Cuando un protocolo no cumple estos requerimientos (por ejemplo, TCP no preserva los delimitadores) nosotros proporcionamos mecanismos para reorganizar los mensajes antes de dejar que el sistema los maneje.

Una estructura de datos del nucleo, el canal es un handle a un servidor de ficheros. Las operaciones sobre un canal generan los mensajes 9P siguientes: Los mensajes session y attach autentican la conexión, establecida por medios externos a 9P, y validan a su usuario. El resultado es un canal autenticado que hace referencia a la raíz del servidor. El mensaje clone crea un nuevo canal idéntico a otro existente, de forma similar a como hace la llamada del sistema dup. Un canal puede ser movido a un fichero del sistema usando el mensaje walk para descender cada nivel en la jerarquía. Los mensajes stat y wstat leen y escriben los atributos del fichero referenciado por el canal. El mensaje open prepara un canal para una posterior operación mediante los mensajes read y write que acceden a los contenidos del fichero. Create y remove realizan las acciones que sus nombres indican sobre el fichero El mensaje clunk descarta un canal sin afectar al fichero.

Un servidor de ficheros residente del nucleo llamado driver mount convierte la versión procedural de 9P en RPCs. La llamada del sistema mount proporciona un descriptor de fichero, que puede ser un pipe a un proceso de usuario o a una conexión de red con una máquina remota, para ser asociado con el punto de montaje. Despues de un mount, las operaciones en el arbol de ficheros bajo el punto de montaje son enviadas como mensajes al servidor de ficheros. El driver mount maneja buffers, empaqueta y desempaqueta parámetros de los mensajes, y desmultiplexa entre procesos que usan el servidor de ficheros.

2.2. Organización del nucleo

El código de red en el nucleo está dividido en tres capas: interfaz con el hardware, procesamiento de protocolos e interfaz con programas. Un driver de dispositivo típicamente usa flujos para conectar las dos capas de interfaz. Modulos de flujos adicionales pueden ser incluidos en un dispositivo para procesar protocolos. Cada driver de dispositivo es un sistema de ficheros residente del nucleo que contiene solamente unos pocos ficheros: por ejemplo, representamos cada UART por medio de un fichero control y otro data.

Los dispositivos multiplexados presentan un interfaz de estructura un poco más compleja. Por ejemplo, el driver Ethernet LANCE sirve un árbol de ficheros de dos niveles (Figura 1) proporcionando

∙ control y configuración de dispositivo

∙ protocolos de nivel usuario como ARP

∙ interfaces de diagnóstico para software de snooping

El directorio superior contiene un fichero clone y un directorio para cada conexión, numerados desde 1 a n. Cada directorio de conexión corresponde a un tipo de paquete Ethernet.

?????? Opening the clone file finds an unused connection directory and opens its ctl. file.

Abrir el fichero clone hace que se encuentre un directorio de una conexión no utilizada y que se abra su fichero ctl. ?????????

Leyendo el fichero control se obtiene el numero de la conexión en ASCII; el proceso usuario puede usar este valor para construir el nombre del directorio de la conexión. En cada directorio de conexión, ficheros llamados ctl, data, stats, y type proporcionan acceso a la conexión. Escribir la cadena connect 2048 en el fichero ctl hace que se ponga el tipo de paquete a 2048 y configura la conexión para recibir todos los paquetes IP enviados a la máquina. Las posteriores lecturas del fichero type obtendrán la cadena 2048. El fichero data accede al medio: leyéndolo se obtiene el siguiente paquete del tipo seleccionado. Escribiendo en él se pone se agrega a la cola un paquete para su transmisión, después de agregar una cabecera que contiene la dirección de origen y el tipo de paquete. El fichero stats devuelve un texto ASCII que contiene la dirección del interfaz, la cuenta de paquetes de entrada y de salida, las estadísticas de error, e información general sobre el estado del interfaz. ?????????? ?????.so tree.pout ???????????

Si varias conexiones en un interfaz están configuradas para un tipo concreto de paquete, cada una recibe una copia de los paquetes entrantes. El tipo especial de paquete -1 selecciona todos los paquetes. Si se escriben las cadenas promiscuous y connect -1 en el fichero ctl se configura una conversación para recibir todos los paquetes del Ethernet.

Aunque el interfaz del driver pueda parecer muy elaborado, la representación de un dispositivo como un conjunto de ficheros que usan cadenas ASCII para comunicarse tiene varias ventajas. Cualquier mecanismo que soporte acceso remoto a ficheros permite inmediatamente a una máquina remota utilizar nuestros interfaces como pasarela. Usar cadenas ASCII para controlar el interfaz evita problemas de orden de byte y asegura una representación uniforme para dispositivos en la misma máquina e incluso permite que los dispositivos sean accedidos remotamente. Representar dispositivos diferentes con el mismo conjunto de ficheros permite que herramientas comunes sirvan para varias redes o interfaces. Programas como stty son reemplazados por echo y una redirección de la shell

2.3. Dispositivos de protocolo

Las conexiones de red se representan mediante pseudo-dispositivos llamados dispositivos de protocolo. Hay drivers para dispositivos de protocolo para el protocolo URP Datakit y para cada uno de los protocolos de Internet, TCP, UDP e IL. IL, que describimos más adelante, es un nuevo protocolo de comunicación usado por Plan 9 para transmitir RPC’s de sistemas de ficheros. Todos los dispositivos de protocolo parecen idénticos, así que los programas usuarios no contienen código específico de red.

Cada driver de dispositivo de protocolo sirve una estructura de directorios similar a la del driver de Ethernet. El directorio superior contiene un fichero clone y un directorio para cada conexión numeradas de 0 a n. Cada directorio de conexión contiene ficheros para controlar una conexión y para enviar y recibir información. Un directorio de conexión TCP tiene el siguiente aspecto:

Para establecer una conexión se siguen estos pasos:

1) El dispositivo clone del protocolo apropiado se abre para reservar una conexión no usada.

2) El descriptor de fichero devuelto por la orden open apunta al fichero ctl de la nueva conexión. Leyendo este fichero se obtiene una cadena ASCII que contiene el número de la conexión.

3) Una cadena ASCII específica de la red y del protocolo se escribe en el fichero ctl.

4) El path del fichero data se construye usando el número de conexión. Cuando el fichero data se abre, la conexión está establecida.

Un proceso puede leer y escribir este descriptor de fichero para enviar y recibir mensajes en la red. Si el proceso abre el fichero listen se bloquea hasta que reciba una llamada entrante. Una cadena de dirección escrita en ctl antes de abrir listen selecciona el puerto o servicio que el proceso está listo para aceptar. Cuando se recibe una llamada, la apertura se completa y se devuelve un descriptor de fichero apuntando al fichero ctl de la nueva conexión. Leer del fichero ctl proporciona un número de conexión que se usa para construir el path del fichero data. Una conexión permanece establecida mientras cualquiera de los ficheros del directorio de la misma están referenciados, o hasta que se recibe un close desde la red.

2.4. Corrientes

Una stream o corriente [Rit84a][Presotto] es un canal bidireccional que conecta un dispositivo (físico o no) con un proceso de usuario.El proceso de usuario inserta y retira datos en un lado de la corriente. Los procesos del nucleo que actúan en nombre de un dispositivo insertan datos al otro lado. Los canales de comunicación asíncrona como pipes, conversaciones TCP, conversaciones Datakit, y lineas RS232 se implementan usando corrientes.

Una corriente comprende una lista lineal de módulos en proceso. Cada módulo tiene una corriente de subida (hacia el proceso) y una de bajada (hacia el dispositivo) rutina put. Llamando a la rutina put del módulo en cualquiera de ambos lados se insertan datos en la corriente. ??????????? Cada módulo llama al siguiente para que envie datos hacia arriba o hacia abajo en la corriente. Each module calls the succeeding one to send data up or down the stream. ??????????

Una instancia de un módulo en proceso se representa por un par de colas, una para cada dirección. Las colas apuntan a las rutinas put y pueden usarse para encolar información que va a pasarse por la corriente. Algunas rutinas put encolan los datos localmente y los envian a través de la corriente más tarde, bien sea por una llamada posterior o por un evento asíncrono como un temporizador de retransmisión o una interrupción de dispositivo. Los módulos en proceso crean procesos ayudantes del nucleo que proporcionan un contexto para manejar los eventos asíncronos.Por ejemplo, un proceso ayudante del nucleo se despierta periodicamente para realizar cualquier retransmisión TCP necesaria. ???????? El uso de procesos del nucleo en lugar de rutinas de servicio que se ejecutan hasta que terminan difiere de la implementación de las corrientes en UNIX. The use of kernel processes instead of serialized run-to-completion service routines differs from the implementation of Unix streams. ???????? Las rutinas de servicio de UNIX no pueden usar ningún recurso bloqueante del nucleo y carecen de un estado local prolongado. Los procesos ayudantes del nucleo resuelven estos problemas simplificando el código de las corrientes.

No hay sincronización implícita en nuestras corrientes. Cada módulo en proceso debe asegurarse de que los procesos concurrentes que usan la corriente están sincronizados. Esto maximiza la concurrencia, pero introduce la posibilidad de un bloqueo mutuo. Sin embargo, los bloqueos mutuos se evitan fácilmente mediante la programación cuidadosa; hasta la fecha no han causado problemas.

La información se representa por medio de listas enlazadas de estructuras del nucleo llamadas bloques. Cada bloque contiene un tipo, algunas banderas de estado, y punteros a un buffer opcional. Los buffers de los bloques pueden guardar datos o información de control, es decir, directivas para los módulos en proceso. Los bloques y los buffers de bloque se asignan dinámicamente de la memoria del nucleo.

2.4.1. Interfaz de usuario

Una corriente se representa a nivel de usuario por dos ficheros, ctl y data. Los nombres pueden ser cambiados por el driver de dispositivo que use la corriente, como ya dijimos en el ejemplo del driver de UART. El primer proceso que abre cualquiera de ellos crea la corriente automáticamente. El último close la destruye. Escribiendo en el fichero data ???????? se copian datos en los bloques del nucleo y se pasan a la rutina put del primer módulo en proceso. copies the data into kernel blocks and passes them to the downstream put routine of the first processing module. ????????? Se garantiza que una escritura de menos de 32K cabe en un solo bloque. Las escrituras concurrentes en la misma corriente no están sincronizadas, aunque el tamaño de 34K asegura escrituras atómicas para la mayoría de los protocolos. El último bloque escrito es marcado con un delimitador para alertar a los módulos de la corriente que se ocupan de los límites de la escritura. La mayor parte de las veces, la primera rutina put llama a la segunda, la segunda a la tercera y así hasta que los datos salen. Como consecuencia, la mayoría de los datos salen sin necesidad de un cambio de contexto.

Leer del fichero data devuelve los datos encolados en lo alto de la corriente. La lectura termina cuando se alcanza la cuenta de lecturas o cuando se encuentra el final de un bloque delimitado. Un bloqueo de lectura por corriente asegura que solamente un proceso puede leer de una corriente a la vez, y garantiza que los bytes leidos llegan contiguos desde la corriente.

Al igual que las corrientes UNIX [Rit84a], las de Plan 9 pueden ser configuradas dinámicamente. El sistema de corrientes intercepta e interpreta los siguientes bloques de control:

push name agrega una instancia del modulo en proceso name en lo alto de la corriente.

pop retira el módulo superior de la stream.

hangup ??????? envía un mensaje de colgar por la corriente desde el lado del dispositivo sends a hangup message up the stream from the device end. ???????

Otros bloques de control son específicos de cada módulo y son interpretados por cada modulo en proceso sobre la marcha.

La enrevesada sintaxis y semántica de la llamada del sistema ioctl de UNIX nos convenció para eliminarla de Plan 9. En realidad, ioctl es reemplazada por el fichero ctl. Escribir en él es idéntico a escribir en el fichero data solo que los bloques son de tipo control. Un módulo en proceso analiza cada bloque de control que ve. Los comandos en los bloques de control son cadenas ASCII, así que el orden de bytes no es un problema cuando un sistema controla corrientes en un espacio de nombres implementado en otro procesador. El tiempo para analizar bloques de control no es importante, puesto que las operaciones de control son raras.

2.4.2. Device Interface

The module at the downstream end of the stream is part of a device interface. The particulars of the interface vary with the device. Most device interfaces consist of an interrupt routine, an output put routine, and a kernel process. The output put routine stages data for the device and starts the device if it is stopped. The interrupt routine wakes up the kernel process whenever the device has input to be processed or needs more output staged. The kernel process puts information up the stream or stages more data for output. The division of labor among the different pieces varies depending on how much must be done at interrupt level. However, the interrupt routine may not allocate blocks or call a put routine since both actions require a process context.

2.4.3. Multiplexing

The conversations using a protocol device must be multiplexed onto a single physical wire. We push a multiplexer processing module onto the physical device stream to group the conversations. The device end modules on the conversations add the necessary header onto downstream messages and then put them to the module downstream of the multiplexer. The multiplexing module looks at each message moving up its stream and puts it to the correct conversation stream after stripping the header controlling the demultiplexing.

This is similar to the Unix implementation of multiplexer streams. The major difference is that we have no general structure that corresponds to a multiplexer. Each attempt to produce a generalized multiplexer created a more complicated structure and underlined the basic difficulty of generalizing this mechanism. We now code each multiplexer from scratch and favor simplicity over generality.

2.4.4. Reflections

Despite five year’s experience and the efforts of many programmers, we remain dissatisfied with the stream mechanism. Performance is not an issue; the time to process protocols and drive device interfaces continues to dwarf the time spent allocating, freeing, and moving blocks of data. However the mechanism remains inordinately complex. Much of the complexity results from our efforts to make streams dynamically configurable, to reuse processing modules on different devices and to provide kernel synchronization to ensure data structures don’t disappear under foot. This is particularly irritating since we seldom use these properties.

Streams remain in our kernel because we are unable to devise a better alternative. Larry Peterson’s X-kernel [Pet89a] is the closest contender but doesn’t offer enough advantage to switch. If we were to rewrite the streams code, we would probably statically allocate resources for a large fixed number of conversations and burn memory in favor of less complexity.

3. The IL Protocol

None of the standard IP protocols is suitable for transmission of 9P messages over an Ethernet or the Internet. TCP has a high overhead and does not preserve delimiters. UDP, while cheap, does not provide reliable sequenced delivery. Early versions of the system used a custom protocol that was efficient but unsatisfactory for internetwork transmission. When we implemented IP, TCP, and UDP we looked around for a suitable replacement with the following properties:

∙ Reliable datagram service with sequenced delivery

∙ Runs over IP

∙ Low complexity, high performance

∙ Adaptive timeouts

None met our needs so a new protocol was designed. IL is a lightweight protocol designed to be encapsulated by IP. It is a connection-based protocol providing reliable transmission of sequenced messages between machines. No provision is made for flow control since the protocol is designed to transport RPC messages between client and server. A small outstanding message window prevents too many incoming messages from being buffered; messages outside the window are discarded and must be retransmitted. Connection setup uses a two way handshake to generate initial sequence numbers at each end of the connection; subsequent data messages increment the sequence numbers allowing the receiver to resequence out of order messages. In contrast to other protocols, IL does not do blind retransmission. If a message is lost and a timeout occurs, a query message is sent. The query message is a small control message containing the current sequence numbers as seen by the sender. The receiver responds to a query by retransmitting missing messages. This allows the protocol to behave well in congested networks, where blind retransmission would cause further congestion. Like TCP, IL has adaptive timeouts. A round-trip timer is used to calculate acknowledge and retransmission times in terms of the network speed. This allows the protocol to perform well on both the Internet and on local Ethernets.

In keeping with the minimalist design of the rest of the kernel, IL is small. The entire protocol is 847 lines of code, compared to 2200 lines for TCP. IL is our protocol of choice.

4. Network Addressing

A uniform interface to protocols and devices is not sufficient to support the transparency we require. Since each network uses a different addressing scheme, the ASCII strings written to a control file have no common format. As a result, every tool must know the specifics of the networks it is capable of addressing. Moreover, since each machine supplies a subset of the available networks, each user must be aware of the networks supported by every terminal and server machine. This is obviously unacceptable.

Several possible solutions were considered and rejected; one deserves more discussion. We could have used a user-level file server to represent the network name space as a Plan 9 file tree. This global naming scheme has been implemented in other distributed systems. The file hierarchy provides paths to directories representing network domains. Each directory contains files representing the names of the machines in that domain; an example might be the path /net/name/usa/edu/mit/ai. Each machine file contains information like the IP address of the machine. We rejected this representation for several reasons. First, it is hard to devise a hierarchy encompassing all representations of the various network addressing schemes in a uniform manner. Datakit and Ethernet address strings have nothing in common. Second, the address of a machine is often only a small part of the information required to connect to a service on the machine. For example, the IP protocols require symbolic service names to be mapped into numeric port numbers, some of which are privileged and hence special. Information of this sort is hard to represent in terms of file operations. Finally, the size and number of the networks being represented burdens users with an unacceptably large amount of information about the organization of the network and its connectivity. In this case the Plan 9 representation of a resource as a file is not appropriate.

If tools are to be network independent, a third-party server must resolve network names. A server on each machine, with local knowledge, can select the best network for any particular destination machine or service. Since the network devices present a common interface, the only operation which differs between networks is name resolution. A symbolic name must be translated to the path of the clone file of a protocol device and an ASCII address string to write to the ctl file. A connection server (CS) provides this service.

4.1. Network Database

On most systems several files such as /etc/hosts, /etc/networks, /etc/services, /etc/hosts.equiv, /etc/bootptab, and /etc/named.d hold network information. Much time and effort is spent administering these files and keeping them mutually consistent. Tools attempt to automatically derive one or more of the files from information in other files but maintenance continues to be difficult and error prone.

Since we were writing an entirely new system, we were free to try a simpler approach. One database on a shared server contains all the information needed for network administration. Two ASCII files comprise the main database: /lib/ndb/local contains locally administered information and /lib/ndb/global contains information imported from elsewhere. The files contain sets of attribute/value pairs of the form attr=value, where attr and value are alphanumeric strings. Systems are described by multi-line entries; a header line at the left margin begins each entry followed by zero or more indented attribute/value pairs specifying names, addresses, properties, etc. For example, the entry for our CPU server specifies a domain name, an IP address, an Ethernet address, a Datakit address, a boot file, and supported protocols.

4.2. Connection Server

On each system a user level connection server process, CS, translates symbolic names to addresses. CS uses information about available networks, the network database, and other servers (such as DNS) to translate names. CS is a file server serving a single file, /net/cs. A client writes a symbolic name to /net/cs then reads one line for each matching destination reachable from this system. The lines are of the form filename message, where filename is the path of the clone file to open for a new connection and message is the string to write to it to make the connection. The following example illustrates this. Ndb/csquery is a program that prompts for strings to write to /net/cs and prints the replies.

CS provides meta-name translation to perform complicated searches. The special network name net selects any network in common between source and destination supporting the specified service. A host name of the form $attr is the name of an attribute in the network database. The database search returns the value of the matching attribute/value pair most closely associated with the source host. Most closely associated is defined on a per network basis. For example, the symbolic name tcp!$auth!rexauth causes CS to search for the auth attribute in the database entry for the source system, then its subnetwork (if there is one) and then its network.

Normally CS derives naming information from its database files. For domain names however, CS first consults another user level process, the domain name server (DNS). If no DNS is reachable, CS relies on its own tables.

Like CS, the domain name server is a user level process providing one file, /net/dns. A client writes a request of the form domain-name type, where type is a domain name service resource record type. DNS performs a recursive query through the Internet domain name system producing one line per resource record found. The client reads /net/dns to retrieve the records. Like other domain name servers, DNS caches information learned from the network. DNS is implemented as a multi-process shared memory application with separate processes listening for network and local requests.

5. Library routines

The section on protocol devices described the details of making and receiving connections across a network. The dance is straightforward but tedious. Library routines are provided to relieve the programmer of the details.

5.1. Connecting

The dial library call establishes a connection to a remote destination. It returns an open file descriptor for the data file in the connection directory.

dest is the symbolic name/address of the destination.

local is the local address. Since most networks do not support this, it is usually zero.

dir is a pointer to a buffer to hold the path name of the protocol directory representing this connection. Dial fills this buffer if the pointer is non-zero.

cfdp is a pointer to a file descriptor for the ctl file of the connection. If the pointer is non-zero, dial opens the control file and tucks the file descriptor here.

Most programs call dial with a destination name and all other arguments zero. Dial uses CS to translate the symbolic name to all possible destination addresses and attempts to connect to each in turn until one works. Specifying the special name net in the network portion of the destination allows CS to pick a network/protocol in common with the destination for which the requested service is valid. For example, assume the system research.bell-labs.com has the Datakit address nj/astro/research and IP addresses 135.104.117.5 and 129.11.4.1. The call

Dial accepts addresses instead of symbolic names. For example, the destinations tcp!135.104.117.5!513 and tcp!research.bell-labs.com!login are equivalent references to the same machine.

5.2. Listening

A program uses four routines to listen for incoming connections. It first announce()s its intention to receive connections, then listen()s for calls and finally accept()s or reject()s them. Announce returns an open file descriptor for the ctl file of a connection and fills dir with the path of the protocol directory for the announcement.

Listen returns an open file descriptor for the ctl file and fills ldir with the path of the protocol directory for the received connection. It is passed dir from the announcement.

Accept and reject are called with the control file descriptor and ldir returned by listen. Some networks such as Datakit accept a reason for a rejection; networks such as IP ignore the third argument.

The following code implements a typical TCP listener. It announces itself, listens for connections, and forks a new process for each. The new process echoes data on the connection until the remote end closes it. The "*" in the symbolic name means the announcement is valid for any addresses bound to the machine the program is run on.

6. User Level

Communication between Plan 9 machines is done almost exclusively in terms of 9P messages. Only the two services cpu and exportfs are used. The cpu service is analogous to rlogin. However, rather than emulating a terminal session across the network, cpu creates a process on the remote machine whose name space is an analogue of the window in which it was invoked. Exportfs is a user level file server which allows a piece of name space to be exported from machine to machine across a network. It is used by the cpu command to serve the files in the terminal’s name space when they are accessed from the cpu server.

By convention, the protocol and device driver file systems are mounted in a directory called /net. Although the per-process name space allows users to configure an arbitrary view of the system, in practice their profiles build a conventional name space.

6.1. Exportfs

Exportfs is invoked by an incoming network call. The listener (the Plan 9 equivalent of inetd) runs the profile of the user requesting the service to construct a name space before starting exportfs. After an initial protocol establishes the root of the file tree being exported, the remote process mounts the connection, allowing exportfs to act as a relay file server. Operations in the imported file tree are executed on the remote server and the results returned. As a result the name space of the remote machine appears to be exported into a local file tree.

The import command calls exportfs on a remote machine, mounts the result in the local name space, and exits. No local process is required to serve mounts; 9P messages are generated by the kernel’s mount driver and sent directly over the network.

Exportfs must be multithreaded since the system calls open, read and write may block. Plan 9 does not implement the select system call but does allow processes to share file descriptors, memory and other resources. Exportfs and the configurable name space provide a means of sharing resources between machines. It is a building block for constructing complex name spaces served from many machines.

The simplicity of the interfaces encourages naive users to exploit the potential of a richly connected environment. Using these tools it is easy to gateway between networks. For example a terminal with only a Datakit connection can import from the server helix:

6.2. Ftpfs

We decided to make our interface to FTP a file system rather than the traditional command. Our command, ftpfs, dials the FTP port of a remote system, prompts for login and password, sets image mode, and mounts the remote file system onto /n/ftp. Files and directories are cached to reduce traffic. The cache is updated whenever a file is created. Ftpfs works with TOPS-20, VMS, and various Unix flavors as the remote system.

7. Cyclone Fiber Links

The file servers and CPU servers are connected by high-bandwidth point-to-point links. A link consists of two VME cards connected by a pair of optical fibers. The VME cards use 33MHz Intel 960 processors and AMD’s TAXI fiber transmitter/receivers to drive the lines at 125 Mbit/sec. Software in the VME card reduces latency by copying messages from system memory to fiber without intermediate buffering.

8. Performance

We measured both latency and throughput of reading and writing bytes between two processes for a number of different paths. Measurements were made on two- and four-CPU SGI Power Series processors. The CPUs are 25 MHz MIPS 3000s. The latency is measured as the round trip time for a byte sent from one process to another and back again. Throughput is measured using 16k writes from one process to another.

9. Conclusion

The representation of all resources as file systems coupled with an ASCII interface has proved more powerful than we had originally imagined. Resources can be used by any computer in our networks independent of byte ordering or CPU type. The connection server provides an elegant means of decoupling tools from the networks they use. Users successfully use Plan 9 without knowing the topology of the system or the networks they use. More information about 9P can be found in the Section 5 of the Plan 9 Programmer’s Manual, Volume I.

10. References

[Pike90] R. Pike, D. Presotto, K. Thompson, H. Trickey, ‘‘Plan 9 from Bell Labs’’, UKUUG Proc. of the Summer 1990 Conf. , London, England, 1990.

[Needham] R. Needham, ‘‘Names’’, in Distributed systems, S. Mullender, ed., Addison Wesley, 1989.

[Presotto] D. Presotto, ‘‘Multiprocessor Streams for Plan 9’’, UKUUG Proc. of the Summer 1990 Conf. , London, England, 1990.

[Met80] R. Metcalfe, D. Boggs, C. Crane, E. Taf and J. Hupp, ‘‘The Ethernet Local Network: Three reports’’, CSL-80-2, XEROX Palo Alto Research Center, February 1980.

[Fra80] A. G. Fraser, ‘‘Datakit - A Modular Network for Synchronous and Asynchronous Traffic’’, Proc. Int’l Conf. on Communication, Boston, June 1980.

[Pet89a] L. Peterson, ‘‘RPC in the X-Kernel: Evaluating new Design Techniques’’, Proc. Twelfth Symp. on Op. Sys. Princ., Litchfield Park, AZ, December 1990.

[Rit84a] D. M. Ritchie, ‘‘A Stream Input-Output System’’, AT&T Bell Laboratories Technical Journal, 68(8), October 1984.

Notes

Originalmente aparecido en Proc. of the Winter 1993 USENIX Conf., pp. 271-280, San Diego, CA