home *** CD-ROM | disk | FTP | other *** search
/ ftp.cse.unsw.edu.au / 2014.06.ftp.cse.unsw.edu.au.tar / ftp.cse.unsw.edu.au / pub / doc / misc / apollo / ost < prev   
Encoding:
Text File  |  1992-10-18  |  34.8 KB  |  606 lines

  1.  
  2.  
  3. To be presented at Summer 1986 Usenix Conference, Atlanta, Georgia.
  4.  
  5. An Extensible I/O System
  6. Jim Rees, Paul H. Levine, Nathaniel Mishkin, Paul J. Leach
  7.  
  8. apollo computer inc
  9. 330 Billerica Road
  10. Chelmsford, MA 01824
  11.  
  12. Introduction
  13.  
  14. For years, programming environments have provided device independent program I/O.
  15. The programmer normally codes file I/O requests using a standard set of procedure
  16. calls, such as the Unixznes1
  17.  
  18. While this concept has been around for a long time, the systems that implemented
  19. the concept have generally had one major shortcoming. The only way to add a new type
  20. of I/O target to the system was to modify the system source. In the case of Unix
  21. operating systems, for example, it is necessary to modify and rebuild the operating
  22. system kernel and to have all of the software that implements the management of the
  23. new I/O target permanently wired into physical memory. Most schemes for adding new
  24. file types to the Unix kernel operate at the file system level, so that within a
  25. given file system, all files have the same type.  Further, whenever a new type is
  26. added, various pieces of the system have to be modified to behave correctly with
  27. respect to the new type. Because of this sizable burden, programmers are discouraged
  28. from defining numerous I/O target types.
  29.  
  30. Our goal was to create a framework in which file I/O could be truly extensible -
  31. to allow users to define new types without modification to the basic system. Our
  32. work consisted of building a general framework for extensibility and then applying
  33. those techniques to stream I/O. We call the framework a typed object management system;
  34. and the associated file I/O facility Extensible Streams (ES). The combination of
  35. these two is called the DOMAINr Open Systems Toolkit.
  36.  
  37. The system resulting from our work is novel because it:
  38.  
  39. D Supports (relatively large) typed, permanent, sharable objects in a distributed
  40. file system.
  41.  
  42. D Allows users to define new types of objects.
  43.  
  44. D Allows users to associate generic procedures (operations) with types; the procedures
  45. are dynamically loaded into the address space of processes when the procedure is
  46. invoked.
  47.  
  48. The Open Systems Toolkit allows users to extend the DOMAIN file system by inventing
  49. new file types and writing managers for these types. The current implementation allows
  50. dynamic creation of new types, and dynamic binding of typed objects to the managers
  51. which implement their behavior. Type managers are written and debugged as user programs
  52. and require no kernel modifications for installation. This system has been used successfully
  53. to write and debug new device drivers, to add new types of files, and to provide
  54. remote file system interconnects to foreign file systems.
  55.  
  56. DOMAIN Architecture
  57.  
  58. The DOMAIN system [1] is an architecture for networks of personal workstations and
  59. server computers that creates an integrated distributed computing environment. A
  60. major component of this distributed system is a distributed file system [2] which
  61. consists of four major components: the object storage system, mapped file management,
  62. concurrency control and naming service.
  63.  
  64. The DOMAIN distributed object storage system (OSS) provides location transparent
  65. typed object management across a network of loosely coupled machines. We say "object"
  66. rather than file to specifically include all of the named non-disk objects in a computing
  67. environment, such as devices (serial I/O lines, magtapes, null, etc.), IPC facilites
  68. (sockets, etc.) and processes. While a naming service manages a network-wide heirarchical
  69. name space, at the OSS level objects are named by a 64-bit unique identifier (UID).
  70. The UID consists of a time stamp and a unique node ID. This guarantees that the UID
  71. is unique across all DOMAIN nodes for all time.
  72.  
  73. A 64-bit object type UID is associated with every object. This type is used to divide
  74. the set of all objects into classes of like objects; all of the objects in a class
  75. have common properties and must be operated upon by a single set of procedures. We
  76. use a UID (rather than any other kind of type identifier) because a system facility
  77. supports the unique creation of these 64-bit numbers across all Apollo products.
  78. In the basic DOMAIN system there are several types, including ASCII text, binary,
  79. directory, and record. This strong typing allows the creator of an object to explicitly
  80. specify its intended use and interpretation, rather than depending on the conventions
  81. and cooperation of other users and programs.
  82.  
  83. The DOMAIN OSS supports a consistent set of facilites for naming, locating, creating,
  84. deleting, and providing access control and administration over all objects. Each
  85. object has an inode, which we have extended to contain (among other things) the type
  86. UID of the described object.
  87.  
  88. For disk-based objects OSS also provides storage containers (arrays of pages) for
  89. uninterpreted data. A process accesses this data by handing the kernel the object's
  90. UID and asking for it to be mapped into its address space. The process then uses
  91. ordinary machine instructions to directly manipulate the contents of the object -
  92. the single level store (SLS) concept of Multics [3], Pilot [4], and System/38 [5].
  93.  
  94. Layered on top of the file system is the Streams library, a user state library mapped
  95. into every process's address space, which provides a traditional I/O environment
  96. for programs. The Streams library implements the standard I/O interfaces and so provides
  97. equal access to both disk and non-disk resident objects. The DOMAIN Stream operations
  98. form a superset of the Unix file I/O operations, as they include record-oriented
  99. operations and more inquiry operations but are all based on a file descriptor returned
  100. to callers of open. Streams is an object-oriented facility in that its behavior is
  101. determined by the type of object to which its operations are applied. When a stream
  102. operation is invoked, Streams calls the manager that handles operations for the type
  103. of the object being operated on. Figure 1 diagrams the relationships among the various
  104. pieces of the DOMAIN object management system and Streams.
  105.  
  106. znes1
  107.  
  108. Typed Object Management
  109.  
  110. The fundamental concept underlying the object-oriented part of our system is the
  111. notion that every object is strongly typed and that for each object type there is
  112. a set of executable routines that implement a well-specified group of operations
  113. on that type. This section describes the object typing strategy, defines operations
  114. and describes their partitioning into traits. It also explains the management facilites
  115. necessary to associate typed objects with the code that implements the operations
  116. defined for them.
  117.  
  118. Unix file system objects do not have an explicit type tag, but do keep a form of
  119. type information in several different places. The mode field in a file's inode contains
  120. some bits that distinguish among ordinary files, directories, character and block
  121. special files (devices), and depending on the version of Unix system, FIFOs, sockets,
  122. textual links, and other types of file system objects. There may also be type information
  123. coded into the major and minor device numbers to, for example, distinguish between
  124. tape drives and disk drives. In some cases, type information is encoded in the first
  125. few bits of the file data itself. For instance, there may be "magic numbers" for
  126. tagging various flavors of executable (a.out) files.
  127.  
  128. In the DOMAIN system, the type tag is a UID which is explicitly attached to the object
  129. at the time that the object is created. This provides the advantage of a single,
  130. common mechanism to distinguish among all types. The use of a UID (rather than a
  131. small integer) allows the arbitrary creation of new types without appealing to a
  132. central authority.
  133.  
  134. The fundamental concept underlying the object-oriented part of our system is the
  135. notion of an object type as a set of legal states together with a collection of operations
  136. that implement the state transitions. Operations can be viewed in two ways: as a
  137. specification of how to invoke a transformation on the state of an object, or as
  138. the executable code that performs the transformation. The collection of code that
  139. implements the set of operations for an object type is known as that object type's
  140. type manager.
  141.  
  142. A trait is an ordered set of operations. It represents a kind of behavior that a
  143. client desires from an object. For example, the operations open, close, read and
  144. write could be a "stream-like" trait, and the operations set speed and echo input
  145. could be part of a "tty-like" trait. An object supports a trait if its type manager
  146. implements the operations of the trait. For every trait that a type manager supports,
  147. the manager provides an entry point vector (EPV), that is an ordered list of pointers
  148. to the procedures that implement the operations in the trait.
  149.  
  150. znes1
  151.  
  152. The implementation of the typed object management system has two main components:
  153. the type system and the trait system. We use the name Trait/Types/Managers (TTM)
  154. to refer to these two components plus the set of all type managers.
  155.  
  156. The type system is responsible for maintaining a data base containing mappings between
  157. type UID, type manager, and type name. New types can be created at will. For convenience,
  158. there is a name for every type, but a type UID rather than a type name is actually
  159. attached to the file system objects. This guarantees that all types are unique, even
  160. if two different implementors independently choose the same name. The type system
  161. provides procedures that can be used to create new types, associate a name with a
  162. type, and look up type UID of a given type name. It can also find the manager for
  163. a given type.
  164.  
  165. The role of the trait system is to bind <object, trait> pairs to type manager EPVs.
  166. It provides the trait_$bind call for this purpose. This call looks up the object's
  167. type UID and then asks the type system for the corresponding type manager. Object
  168. code libraries containing managers are not pre-linked with client object code. Rather,
  169. the trait system is responsible for dynamically loading them into the address space
  170. of clients as necessary. To perform this task, the trait manager uses the type system
  171. to locate the manager object code file. It then loads the manager into the address
  172. space of the client. The type manager is linked as an autonomous program whose main
  173. entry point is called when the manager is loaded. The code at this entry point registers
  174. all supported <trait, EPV> pairs with the trait system. Once the manager is loaded,
  175. the trait system returns the requested EPV to its client.
  176.  
  177. The type definition for an EPV corresponding to a trait that describes operations
  178. on stacks might look like:
  179.  
  180. typedef struct {
  181.     void        (*push)(uid_$t, stack_$elem_t);
  182.     stack_$elem_t   (*pop) (uid_$t);
  183. } stack_$epv;
  184.  
  185. The actual EPV for a type manager that supported the stack trait would be declared
  186. as:
  187.  
  188. stack_$epv my_stack_epv = {
  189.     my_push,
  190.     my_pop,
  191. };
  192.  
  193. where my_push and my_pop are the names of real procedures that implement the push
  194. and pop operations:
  195.  
  196. void my_push(obj, elem)
  197. uid_$t  obj;
  198. stack_$elem_t elem;
  199. {
  200. ...
  201. }
  202.  
  203. stack_$elem_t my_pop(obj)
  204. uid_$t  obj;
  205. {
  206. ...
  207. }
  208.  
  209. The client uses trait_$bind to get a pointer to an EPV from the trait system:
  210.  
  211. trait_$epv *trait_$bind(obj, trait, typuidp, statusp)
  212.  
  213. uid_$t  obj;        /* IN:  object we want to operate on */
  214. trait_$t    trait;      /* IN:  trait we want to use */
  215. uid_$t  *typuidp;       /* OUT: type of object */
  216. status_$t   *statusp;       /* OUT: status */
  217.  
  218. Once a client has called trait_$bind and received an EPV, it can invoke operations
  219. on the object. For example, to call the push and pop operations in the sample trait
  220. above:
  221.  
  222. epv = (stack_$epv *) trait_$bind(my_obj, stack_$trait, &type_uid, &status);
  223. (*(epv->push))(my_obj, an_elem);
  224. an_elem = (*(epv->pop))(my_obj);
  225.  
  226. The DOMAIN system provides a set of programs for creating and installing new types
  227. and their managers. A user who creates a new type will also typically write a type
  228. manager for that type. The manager is written as a set of subroutines, each implementing
  229. an operation for the traits that the manager supports. The programmer can use the
  230. standard debugging tools on the type manager. The manager is installed by running
  231. a program that puts the executable code in a well-known place and registers the new
  232. manager with the type system data base. No kernel modifications are required, and
  233. the machine does not have to be rebooted. There is no limit on the number of object
  234. types a single system may support since their managers are only loaded when needed.
  235.  
  236. Extensible Streams
  237.  
  238. Extensible Streams is a client of TTM. ES defines three basic traits: IO, IO_OC,
  239. and IO_XOC. The IO trait contains the traditional I/O operations - get (read), put
  240. (write), seek, etc. The IO_OC trait contains the operations open and initialize.
  241. (The IO_XOC trait is similar to IO_OC except that it supports extended naming, a
  242. facility that allows non-standard pathnames, described below.) ES also defines a
  243. set of auxiliary traits containing operations that only some type managers will choose
  244. to implement. The current set of auxiliary traits include: SIO (operations for manipulating
  245. serial I/O lines), SOCKET (operations corresponding to the 4.2bsd Unix "socket" system
  246. calls), PAD (operations for manipulating windows), and DIRECTORY (operations for
  247. reading and manipulating directories).
  248.  
  249. ES introduces a layer of abstraction on top of the basic operations. This layer -
  250. called the I/O Switch - supports the notion of an open stream and isolates the user
  251. of file system I/O from the TTM. An open stream is created by calling the I/O Switch
  252. procedure ios_$open which:
  253.  
  254. D Calls trait_$bind to get the IO and IO_OC EPVs for the object being opened.
  255.  
  256. D Calls the manager's open operation. This operation returns a handle - a virtual
  257. address of a descriptor that is meaningful only to the manager. The manager stores
  258. in the handle whatever information it needs in order to maintain the semantics of
  259. an open stream (e.g., position in stream, buffers).
  260.  
  261. D Allocates an entry in the stream table - a table of open streams. Each entry in
  262. this table contains the EPVs for the IO and IO_OC traits, and the handle returned
  263. by the open operation.
  264.  
  265. D Returns the small integer - the file descriptor - that identifies the table entry
  266. allocated in the previous step. This file descriptor is used by the application program
  267. on subsequent calls.
  268.  
  269. Another I/O Switch procedure, ios_$create, is similar to ios_$open except that it
  270. creates a new object and calls the manager's initialize operation. In addition to
  271. returning a handle, the initialize operation stores any information it needs to in
  272. the newly-created object.
  273.  
  274. For each operation in the IO trait, a trivial I/O Switch procedure takes a file descriptor
  275. as its first argument, converts the descriptor to a handle (by consulting the stream
  276. table), and calls the appropriate procedure from the EPV (also obtained from the
  277. stream table). The various forms of I/O (e.g., Unix I/O system calls, FORTRAN and
  278. Pascal language I/O primitives) are implemented in terms of these I/O Switch procedures.
  279.  
  280. Extended Naming
  281.  
  282. Extended naming is a facility that allows the pathname of an object being opened
  283. to be augmented with additional text to be interpreted by the Streams manager of
  284. the object to which the pathname refers. This additional text is called the residual
  285. pathname.
  286.  
  287. If an application calls the I/O Switch's open procedure with a pathname containing
  288. a residual, and the non-residual part of the pathname names an object whose type
  289. manager implements the IO_XOC trait (as opposed to the IO_OC trait), then the I/O
  290. Switch passes the residual to the manager as one of the arguments in the IO_XOC open
  291. operation. The manager is free to interpret the residual in any way it chooses.
  292.  
  293. Program-level I/O based on a simple system naming facility allows an application
  294. program to pass the name of a file system object into the open call, for the I/O
  295. Switch to locate the specified object, and for the manager of that type of object
  296. to then do its job. For example, the pathname /usr/fonts/classic refers to the object
  297. whose name is classic, which is catalogued in the directory whose name is fonts,
  298. which in turn is catalogued in a directory object whose name is usr. The I/O Switch
  299. resolves the entire pathname down into the single target object, and passes a shorthand
  300. identifier for that object to the manager.
  301.  
  302. The intent of extended naming is to allow the object managers themselves to take
  303. over part of the pathname-walking responsibility so that they can manage a collection
  304. of objects that can be distinguished by the remainder of the pathname. To clarify
  305. this notion, consider the follo
  306. ing.
  307.  
  308. The pathname /jim/test.c would normally be interpreted as a file named test.c catalogued
  309. in the directory named jim. The name also suggests that the file is a C language
  310. source file and that all operations that would need to work on such a file (e.g.,
  311. compiling, printing, editing) could be requested by specifying this name.
  312.  
  313. Now let's suppose that file test.c is of type history. The actual file system object
  314. contains the entire change history of the file, much the same way that a SCCS [7]
  315. file does. Programs that do not care about the change history can open this file
  316. and read from it. The open and read requests are passed on by the I/O Switch to the
  317. history type manager, and the manager can be written so that the program always reads
  318. the latest version of the file.
  319.  
  320. Extended naming takes the concept one step further by allo
  321. ing the manger writer for the history object type to allow the specification of additional
  322. pathname text. Where the simply specified pathname results in the reading of data
  323. from the latest version of the file test.c, the manager writer might wish to allow
  324. a naming syntax of the form /jim/test.c/-1 to indicate that the application wishes
  325. to use the penultimate version of the file instead of the newest. The I/O Switch
  326. allows this additional specification to be issued at the application program layer
  327. and passed through to the manager for the target object.
  328.  
  329. The application passes the pathname (with the extended name) to the I/O Switch open
  330. routine. The open routine evaluates the pathname one pathname component at a time
  331. walking from left to right. In the current example, jim is a directory where the
  332. name test.c is located. test.c is discovered to be a history file (not a directory),
  333. and because the original pathname still has remaining text (`-1') that the I/O Switch
  334. cannot resolve, it passes that remainder to the history object manager's IO_XOC open
  335. routine. The history manager is then able to decide what text to provide to subsequent
  336. read requests and the intended result occurs. In this case, the application program
  337. is not affected by the apparent peculiarity of the original pathname. The I/O Switch
  338. avoids confusion by only walking the pathname through objects that support the directory
  339. trait and the manager is able to get whatever information it needs to do the job
  340. it was written to do.
  341.  
  342. Other examples of extended names a history manager might be willing to accept are:
  343.  
  344.     /jim/test.c/03.02.85
  345.     /jim/test.c/original
  346.     /jim/test.c/yesterday
  347.  
  348. Another example of the application of extended naming is a gateway to a non-DOMAIN
  349. file system. For example, imagine an object whose name is THEM and whose type is
  350. UNIX_gate. A pathname of the form /gateways/THEM/usr/jan/test.c could be passed by
  351. an application program to the I/O Switch. The Switch would see that the object named
  352. gateways was a directory and would look the name THEM up in that directory. THEM
  353. would be found to be a UNIX_gate object, and since the Switch cannot walk the pathname
  354. through objects that are not directories, it would call the UNIX_gate object manager's
  355. open routine. That routine is passed the UID for the object whose name is THEM and
  356. the remaining pathname (/usr/jan/test.c). The UNIX_gate manager then has the information
  357. it needs to contact a remote file service for the data it needs to meet the demands
  358. of the requesting application program. The protocol that the manager uses to access
  359. the remote files is entirely up to the manager writer, and because the manager runs
  360. in user space, it is not restricted to kernel services but can use any service available
  361. at the user level. This scheme has been used to build a type manager that interconnects
  362. the DOMAIN file system with a generic Berkeley 4.2 Unix file system, and another
  363. that connects to a VAX/VMS file system.
  364.  
  365. Underlying Facilities
  366.  
  367. Many facilities provided in the DOMAIN environment made the implementation of TTM
  368. and Extensible Streams possible. These facilities make it possible to write OS-like
  369. functions in user space.
  370.  
  371. The underlying virtual memory system - which allows objects to be mapped into the
  372. virtual address space - is needed to give type managers low level, yet controlled,
  373. access to the raw data in objects. The virtual memory system allows more flexible
  374. access to the address space than that allowed by sbrk(2). These calls take the name
  375. of an object, map the object into the address space, and return a pointer to (i.e.
  376. the virtual address of) the mapped object. The address space of a process can be
  377. characterized solely in terms of what objects are mapped where. Processes are not
  378. allowed to make memory references to parts of the address space to which no object
  379. is mapped.
  380.  
  381. The read/write storage (RWS) facility is a flexible and efficient storage allocation
  382. mechanism. It is implemented in user space in terms of the virtual memory primitives;
  383. it maps temporary objects into the address space and allocates storage from that
  384. part of the address space. It allows storage to be allocated from multiple pools.
  385. One pool corresponds exactly to the type of storage allocated by malloc. Another
  386. pool is similar, except its state is not obliterated by exec calls. Type managers
  387. must use storage from this pool to hold per-process state information since open
  388. streams must survive calls to exec.
  389.  
  390. RWS also provides a global storage pool. The global pool is a place where storage
  391. that can be viewed from all processes' address spaces can be allocated. The allocation
  392. call returns a pointer to the allocated storage, and this pointer is valid in all
  393. processes. Type managers must use storage from the global pool to maintain things
  394. like the current position (i.e. offset from beginning-of-file) of an open stream.
  395. If a process opens a stream to an object, forks, and then the child does I/O to the
  396. stream it was passed, the parent sees the position of its stream change too. Thus,
  397. position information must be in storage accessible to both parent and child. Because
  398. type managers run in user space, they need a user space global storage allocater
  399. for this purpose.
  400.  
  401. The dynamic program loader allows the system to load managers as they are needed.
  402. Managers for types that are not used by a given process do not take up any virtual
  403. address space in that process. The loader is implemented in user space in terms of
  404. the RWS facility (to allocate space for static data) and the mapping calls. The pure
  405. parts of executable images are simply mapped into the address space before execution,
  406. because the compilers produce position-independent code.  In 4.2bsd, only the kernel
  407. can be dynamically linked to; all other subroutines must be statically bound to the
  408. program which uses them.
  409.  
  410. The eventcount [8] (EC2) facility is the basic process synchronization mechanism.
  411. Eventcounts are similar to semaphores: eventcounts are associated with significant
  412. events, and processes can advance an eventcount to notify another process that an
  413. event has occurred, or wait on a list of eventcounts until the first event happens.
  414.  
  415. A design principle for all DOMAIN interfaces is that for every potentially blocking
  416. procedure in an interface, there is an associated eventcount that can be obtained
  417. through the interface and that is advanced when the blocking procedure would have
  418. unblocked. This always allows programs to wait for multiple events (say, input on
  419. a TTY line and arrival of a network message) simultaneously. The 4.2bsd select(2)
  420. system call is implemented in terms of eventcounts. However, unlike select, eventcounts
  421. can also be used to wait on non-I/O events, such as process death.
  422.  
  423. The mutual exclusion (MUTEX) facility is a user-state library that contains calls
  424. that allow multiple processes to synchronize their access to shared data (i.e. data
  425. in objects that are mapped into multiple processes). MUTEX is implemented in terms
  426. of EC2. MUTEX defines a lock record that consists of a lock byte and an eventcount.
  427. Typically, applications embed a record of this type in a data structure over which
  428. mutual exclusion must be maintained. A MUTEX lock is set by calling mutex_$lock,
  429. which attempts to set the lock byte (using the hardware test-and-set instruction).
  430. If it fails to set the lock byte, it waits on the eventcount; when the wait returns,
  431. mutex_$lock repeats the attempt to set the lock byte. mutex_$unlock unlocks a MUTEX
  432. lock by clearing the lock byte and advancing the eventcount. Type managers use shared
  433. storage to maintain various kinds of information. To control access to this data,
  434. managers use the MUTEX facility.
  435.  
  436. The shared file control block (SFCB) facility allows multiple processes to coordinate
  437. their access to the same object. There is various dynamic information that processes
  438. might want to keep about an object. For example, type managers need to maintain information
  439. about the object's current length, whether the object is being accessed for read
  440. or write, and whether other processes should be allowed to concurrently access the
  441. object. Since this information must be accessed by multiple processes, it must reside
  442. in global storage. The first process to access the object can allocate the storage,
  443. but how are other processes to find the virtual address of that storage? The SFCB
  444. facility addresses this problem by maintaining a table translating object UID into
  445. global virtual address. (The table is in global storage at a well-known location.)
  446. The sfcb_$get call takes an object UID and returns a pointer to a piece of global
  447. storage (called the SFCB). If no storage was "registered" with SFCB prior to the
  448. call, an SFCB is allocated and registered under the specified UID; otherwise, a pointer
  449. to the existing storage associated with that UID is returned and a use count field
  450. in the storage is incremented to reflect the additional "user" of the storage. sfcb_$free
  451. decrements the use count and, if it reaches zero, frees the storage.
  452.  
  453. Examples
  454.  
  455. Extensible Streams allows a number of special-purpose types to be defined. For example:
  456.  
  457. D History objects: objects that contain many logical versions, only one of which
  458. is presented through the open stream at a time. The residual text is used to specify
  459. a particular version; if omitted, the most recent version is presented. Useful for
  460. source control systems.
  461.  
  462. D Circular objects: objects that grow to a certain size and then have their "oldest"
  463. data discarded when more data is written to them. Useful for maintaining bounded
  464. log output from long-running programs.
  465.  
  466. D Structured documents: objects that contain document control (e.g. font and sectioning)
  467. information but which can be read through an open stream as if they were simple ASCII
  468. text. Useful for using conventional text processing tools (e.g., Unix "grep") [9].
  469.  
  470. D Gateways to non-DOMAIN file systems: objects that are placeholders for entire remote
  471. file systems. The residual is used to specify a particular file on the remote system.
  472. The manager implements whatever network protocol it chooses to access the remote
  473. system's data.
  474.  
  475. D Distributed, replicated data bases: objects that, for reliability reasons, are
  476. distributed across a network of machines. A Yellow Pages [10] manager would eliminate
  477. the need for the ypcat command, and allow any ordinary user to access a Yellow Pages
  478. data base without modification and without having to bind to a special library (the
  479. type manager, in effect, is the library).
  480.  
  481. TTM can be used independently of Extensible Streams. For example, the DOMAIN graphics
  482. library may be converted to use TTM. Currently, the graphics library has code for
  483. all the display hardware types it must support. A TTM-based implementation would
  484. define multiple types, one for each type of display hardware, a trait that contains
  485. graphics operations (e.g. move, draw, trapezoid_fill), and a set of managers, one
  486. per type. This approach would make it possible for only the code necessary for a
  487. particular display hardware type to be loaded into the system, and for the graphics
  488. library to be easily extensible to new hardware types.
  489.  
  490. Experience
  491.  
  492. While the original Streams library was written with the idea of types and type managers
  493. in mind, the actual implementation had to be restructured substantially to take advantage
  494. of TTM. We took this opportunity to redesign the interface to managers and the interface
  495. presented to applications that use the Streams library.
  496.  
  497. The decision to implement the Berkeley socket calls in terms of a trait turned out
  498. to be a good one. On a standard Berkeley Unix system, defining and implementing a
  499. new domain (address family) is a fairly difficult task - it requires working inside
  500. the kernel. With Extensible Streams, you need only create a new type and implement
  501. the SOCKET trait in the manager for that type. We have already implemented a manager
  502. for "DOMAIN domain sockets". Currently, this domain supports only datagram-oriented
  503. sockets (SOCK_DGRAM) because our short-term goal was merely to allow access to specific,
  504. low-level DOMAIN networking primitives using the generic, high-level socket calls.
  505.  
  506.  
  507. The nature of the address family space made our task a bit more complicated. Address
  508. families are identified by small integers in a space over which there is no central
  509. authority. As a result, one has to simply pick an address family out of thin air
  510. and hope no one else has picked it too. It is interesting to contrast this state
  511. of affairs with the type UID approach we took in TTM, since the small integer address
  512. families are essentially type tags. The type UID approach does not have the problem
  513. of more than one person picking the same type tag.  We did not have the option to
  514. change the way address families are identified, so we used a scheme in which address
  515. families are translated into type UIDs.
  516.  
  517. The socket creation primitive is called socket_$create_type. This calls takes a type
  518. UID (and a socket type) and returns a stream to a socket of that type. (socket_$create_type
  519. is analogous to ios_$create except that it calls the create operation in the SOCKET
  520. trait instead of the initialize operation in the IO_OC trait.) The socket system
  521. call converts its address family argument into a type UID by consulting an object
  522. in the file system that contains a table translating address families into type UID.
  523. It then calls socket_$create_type. Note that we could have simply hardcoded a "switch"
  524. statement on address family into the implemention of socket, but this would have
  525. meant that socket would not have been as extensible as we would like. (User-defined
  526. sockets could have been created via socket_$create_type, but not by socket). The
  527. scheme we implemented is less than ideal in that it requires both that the type be
  528. created and that the address-family-to-type-UID object be updated, but it was the
  529. best we could do.
  530.  
  531. One difficult problem that we have not adequately addressed is that of expanding
  532. wildcards in an extended name. For example, using our VMS gateway type manager, one
  533. would like to type the name:
  534.  
  535.     /gateways/my_vms_sys/dra0:[rees.*]mail.txt
  536.  
  537. If my_vms_sys is a gateway object to a VMS system, and dra0:[rees.*]mail.txt is a
  538. VMS file specification, this specification should be expanded to include files named
  539. mail.txt in all subdirectories of dra0:[rees]. Unfortunately, the agent doing the
  540. wildcard expansion (typically the Unix shell) has no knowledge of the syntax of the
  541. extended part of the name, and so has no way to expand the wildcard. We considered
  542. implementing a "wildcard trait," but this is difficult to specify in a general way,
  543. and every program that does wildcard expansion would have to be modified to use this
  544. trait. Instead, we require that standard Unix hierarchical names with "/" separators
  545. be used whenever wildcards are being expanded, but we also allow non-standard syntax
  546. (as in the example above) if there are no wildcards.
  547.  
  548. The semantics of certain Unix operations turned out to be fairly obscure. For example,
  549. suppose a program sets the FAPPEND flag (via fcntl(2)) to "true", then forks, then
  550. the child sets the flag to "false". Is the change to the stream state seen by the
  551. parent as well? We were frequently obliged to look at Unix kernel source or to write
  552. sample programs and run them on a standard Unix system to answer our questions. As
  553. we discuss below, we are led to believe that the task of producing exact semantic
  554. specification is a forbidding one. The various Unix standards committees have their
  555. work cut out for them if they intend to do a complete job.
  556.  
  557. Another interesting experience gained during the implemention of TTM and Extensible
  558. Streams relates to the problem of documentation. The goal of Extensible Streams is
  559. to make it possible for people who are not employees of Apollo Computer to write
  560. new type managers without having access to Apollo source code. This means that the
  561. specification of the semantics of the operations must be very precise - it must completely
  562. characterize the expectations of application programs that do I/O. The creation of
  563. this specification turned out to be a non-trivial task.
  564.  
  565. Acknowledgements
  566.  
  567. In addition to the authors, James Hamilton, David Jabs, and Eric Shienbrood worked
  568. on the implementation of TTM and Extensible Streams. John Yates was involved in some
  569. of the early design work. Elizabeth O'Connell wrote most of the documentation.
  570.  
  571. References
  572.  
  573. [1] Paul J. Leach, Paul H. Levine, Bryan P. Douros, James A. Hamilton, David L. Nelson,
  574. Bernard L. Stumpf, "The Architecture of an Integrated Local Network," IEEE Journal
  575. on Selected Areas in Communication, SAC-1, 5 (November 1983).
  576.  
  577. [2]     Paul J. Leach, Paul H. Levine, James A. Hamilton, Bernard L. Stumpf, "The
  578. File System of an Integrated Local Network," Proceedings of the ACM Computer Science
  579. Conference, New Orleans, La. (March 1985).
  580.  
  581. [3] E. I. Organick, The Multics System: An Examination of Its Structure, M.I.T. Press
  582. (1972).
  583.  
  584. [4] D. D. Redell, Y. K. Dalal, T. R. Horsley, H. C. Lauer, W. C. Lynch, P. R. McJones,
  585. H. G. Murray, S. C. Purcell, "Pilot: An Operating System for a Personal Computer,"
  586. Communications of the ACM, 23, 2 (February 1980).
  587.  
  588. [5] R. E. French, R. W. Collins, L. W. Loen, "System/38 Machine Storage Management,"
  589. IBM System/38 Technical Developments, IBM General Systems Division (1978).
  590.  
  591. [6] Paul J. Leach, Bernard L. Stumpf, James A. Hamilton, Paul H. Levine, "UIDs as
  592. Internal Names in a Distributed File System," Proceedings of the 1st Symposium on
  593. Principles of Distributed Computing, Ottawa, Canada (August 1982).
  594.  
  595. [7] M. J. Rochkind, "The Source Code Control System," IEEE Transactions on Software
  596. Engineering (December 1975).
  597.  
  598. [8] David P. Reed and Rajendra K. Kanodia, "Synchronization with Eventcounts and
  599. Sequencers," Communications of the ACM (February 1979).
  600.  
  601. [9] J. Waldo, "Modelling Text as a Hierarchical Object," Usenix Conference Proceedings,
  602. Atlanta, Ga. (June 1986).
  603.  
  604. [10]    B. Lyon and G. Sager, "Overview of the Sun Network File System," Sun Microsystems,
  605. Inc. (January 1985).
  606.