Annotation of researchv10dc/vol2/streams/streams.ms, revision 1.1

1.1     ! root        1: .so ../ADM/mac
        !             2: .XX streams 503 "A Stream Input-Output System"
        !             3: .TL
        !             4: A Stream Input-Output System\(dg
        !             5: .AU
        !             6: Dennis M. Ritchie
        !             7: .AI
        !             8: .MH
        !             9: .AB
        !            10: In a new version of the
        !            11: .UX
        !            12: operating system,
        !            13: a flexible coroutine-based design replaces the traditional
        !            14: rigid connection between processes and terminals or networks.
        !            15: Processing modules may be inserted dynamically into the stream
        !            16: that connects a user's program to a device.
        !            17: Programs may also connect directly to programs, providing
        !            18: inter-process communication.
        !            19: .AE
        !            20: .2C
        !            21: .FS
        !            22: \(dgThis paper is a reproduction of |reference(bltj streams).
        !            23: .FE
        !            24: .NH
        !            25: Introduction
        !            26: .PP
        !            27: The part of the
        !            28: .UX
        !            29: operating system that deals with terminals
        !            30: and other character devices
        !            31: has always been complicated.
        !            32: In recent versions of the system it has become even more so, for
        !            33: two reasons.
        !            34: .IP 1)
        !            35: Network connections require protocols more ornate than are
        !            36: easily accommodated in the existing structure.
        !            37: A notion of ``line disciplines'' was only partially successful,
        !            38: mostly because in the traditional system only one line discipline
        !            39: can be active at a time.
        !            40: .IP 2)
        !            41: The fundamental data structure of the traditional character I/O system,
        !            42: a queue of individual characters (the ``clist''),
        !            43: is costly because it accepts and dispenses characters one at a time.
        !            44: Attempts to avoid overhead by bypassing the mechanism entirely
        !            45: or by introducing
        !            46: .I "ad hoc
        !            47: routines succeeded in speeding up the
        !            48: code at the expense of regularity.
        !            49: .LP
        !            50: Patchwork solutions to specific problems were
        !            51: destroying the modularity of this part of the system.
        !            52: The time was ripe to redo the whole thing.
        !            53: This paper describes the new organization.
        !            54: .PP
        !            55: The system described here runs on about 20 machines
        !            56: in the Information Sciences Research Division of Bell Laboratories.
        !            57: Although it is being investigated by other parts of Bell Labs,
        !            58: it is not generally available.*
        !            59: .FS
        !            60: *AT&T's UNIX System V Release 3 introduced a reimplementation
        !            61: (in a commercially available system)
        !            62: of the streams mechanisms described here.
        !            63: This version was incomplete; for example, it did not contain a new
        !            64: terminal-handling scheme.
        !            65: UNIX System V Release 4 is intended to complete the streams
        !            66: implementation.
        !            67: The System V version of streams is called STREAMS, and though
        !            68: considerably more ornate, is based on the same ideas that are
        !            69: described here.
        !            70: .FE
        !            71: .NH
        !            72: Overview
        !            73: .PP
        !            74: This section summarizes the nomenclature, components, and mechanisms
        !            75: of the new I/O system.
        !            76: .NH 2
        !            77: Streams
        !            78: .PP
        !            79: A
        !            80: .I "stream
        !            81: is a full-duplex connection between a user's process and a device
        !            82: or pseudo-device.
        !            83: It consists of several linearly connected processing modules,
        !            84: and is analogous to a Shell pipeline, except that
        !            85: data flows in both directions.
        !            86: The modules in a stream communicate almost exclusively
        !            87: by passing messages to their neighbors.
        !            88: Except for some conventional variables used for flow control, modules do not
        !            89: require access to the storage of their neighbors.
        !            90: Moreover, a module provides only one entry point to each neighbor, namely
        !            91: a routine that accepts messages.
        !            92: .PP
        !            93: At the end of the stream closest to the process
        !            94: is a set of routines that provide the interface to the rest of the system.
        !            95: A user's
        !            96: .I "write
        !            97: and
        !            98: I/O control requests are turned into messages sent to the stream,
        !            99: and
        !           100: .I "read
        !           101: requests take data from the stream and pass it to the user.
        !           102: At the other end of the stream is a 
        !           103: device driver module.
        !           104: Here, data arriving from the stream is sent to the device;
        !           105: characters and state transitions detected by the device are
        !           106: composed into messages and sent into the stream towards the user program.
        !           107: Intermediate modules process the messages in various ways.
        !           108: .PP
        !           109: The two end modules in a stream become connected automatically when
        !           110: the device is opened;
        !           111: intermediate modules are attached dynamically by request of the user's program.
        !           112: Stream processing modules are
        !           113: symmetrical; their read and write interfaces are identical.
        !           114: .NH 2
        !           115: Queues
        !           116: .PP
        !           117: Each stream processing module consists of a pair of
        !           118: .I "queues,
        !           119: one for each direction.
        !           120: A queue comprises not only a data queue proper, but also two routines
        !           121: and some status information.
        !           122: One routine is the
        !           123: .I "put procedure,
        !           124: which is called by its neighbor
        !           125: to place messages on the data queue.
        !           126: The other, the
        !           127: .I "service procedure,
        !           128: is scheduled to execute whenever there is work for it to do.
        !           129: The status information includes a pointer to the next queue downstream,
        !           130: various flags, and a pointer to additional state information required
        !           131: by the instantiation of the queue.
        !           132: Queues are allocated in such a way that the routines associated with
        !           133: one half of a stream module may find the queue associated with the other half.
        !           134: (This is used, for example, in generating echos for terminal input.)
        !           135: .NH 2
        !           136: Message blocks
        !           137: .PP
        !           138: The objects passed between queues are blocks obtained from an allocator.
        !           139: Each contains a
        !           140: .I "read pointer,
        !           141: a
        !           142: .I "write pointer,
        !           143: and a
        !           144: .I "limit pointer,
        !           145: which specify respectively the beginning of information being passed, its end,
        !           146: and a bound on the extent to which the write pointer may be increased.
        !           147: .PP
        !           148: The header of a block specifies its type; the most common blocks contain
        !           149: data.
        !           150: There are also control blocks of various kinds,
        !           151: all with the same form as data blocks and obtained from the same
        !           152: allocator.
        !           153: For example, there are control blocks to introduce delimiters
        !           154: into the data stream, to pass user I/O control requests, and to announce
        !           155: special conditions such as line break and carrier loss on terminal
        !           156: devices.
        !           157: .PP
        !           158: Although data blocks arrive in discrete units
        !           159: at the processing modules,
        !           160: boundaries between them are semantically insignificant;
        !           161: standard subroutines may try to coalesce adjacent
        !           162: data blocks in the same queue.
        !           163: Control blocks, however, are never coalesced.
        !           164: .NH 2
        !           165: Scheduling
        !           166: .PP
        !           167: Although each queue module behaves in some ways like a separate process,
        !           168: it is not a real process; the system saves no state information
        !           169: for a queue module that is not running.
        !           170: In particular queue processing routines do not block when they cannot proceed,
        !           171: but must explicitly return control.
        !           172: A queue may be
        !           173: .I "enabled
        !           174: by mechanisms described below.
        !           175: When a queue becomes enabled, the system will, as soon as convenient,
        !           176: call its service procedure entry,
        !           177: which removes successive blocks
        !           178: from the associated data queue, processes them, and places them on the
        !           179: next queue by calling its put procedure.
        !           180: When there are no more blocks to process, or when the next queue becomes
        !           181: full, the service procedure returns to the system.
        !           182: Any special state information must be saved explicitly.
        !           183: .PP
        !           184: Standard routines make enabling of queue modules largely automatic.
        !           185: For example, the routine that puts a block on a queue
        !           186: enables the queue service routine if the queue was empty.
        !           187: .NH 2
        !           188: Flow Control
        !           189: .PP
        !           190: Associated with each queue is a pair of numbers used for flow control.
        !           191: A high-water mark limits the amount of data that may be outstanding
        !           192: in the queue; by convention, modules do not place data on a queue
        !           193: above its limit.
        !           194: A low-water mark is used for scheduling in this way:
        !           195: when a queue has exceeded its high-water mark, a flag is set.
        !           196: Then, when the routine that takes blocks from a data queue notices
        !           197: that this flag is set and that the queue has dropped below the low-water
        !           198: mark, the queue upstream of this one is enabled.
        !           199: .NH
        !           200: Simple Examples
        !           201: .PP
        !           202: Figure 1 depicts a stream device that has just
        !           203: been opened.
        !           204: The top-level routines, drawn as a pair of half-open rectangles
        !           205: on the left, are invoked by users'
        !           206: .I read
        !           207: and
        !           208: .I write
        !           209: calls.
        !           210: The
        !           211: writer
        !           212: routine sends messages to the device driver shown on the right.
        !           213: Data arriving from the device is composed
        !           214: into messages sent to the top-level
        !           215: reader
        !           216: routine, which returns the data to the user process
        !           217: when it executes
        !           218: .I read .
        !           219: .1C
        !           220: .KF
        !           221: .PS
        !           222: boxht = boxht/1.5
        !           223: arrow "user" "write"
        !           224: move up boxht/2
        !           225: line right boxwid/2 then down boxht then left boxwid/2
        !           226: move by (boxwid/2, boxht/2)
        !           227: arrow right
        !           228: line up boxht/2
        !           229: line right boxwid/2 
        !           230: move down boxht
        !           231: line left boxwid/2 then up boxht/2
        !           232: move right boxwid/2
        !           233: arrow "device" "out"
        !           234: move down boxht
        !           235: arrow left "device" "in"
        !           236: move down boxht/2
        !           237: line left boxwid/2 then up boxht
        !           238: move down boxht/2
        !           239: arrow left
        !           240: move up boxht/2
        !           241: line down boxht then left boxwid/2
        !           242: move up boxht/2
        !           243: arrow left "user" "read"
        !           244: .PE
        !           245: .sp .5
        !           246: .ce
        !           247: Figure 1.  Configuration after device open.
        !           248: .sp
        !           249: .KE
        !           250: .2C
        !           251: .PP
        !           252: Figure 2 shows an ordinary terminal connected by an RS-232 line.
        !           253: Here a processing module (the pair of rectangles in the middle) is interposed;
        !           254: it performs the services necessary to make terminals usable,
        !           255: for example echoing, character-erase and line-kill, tab expansion
        !           256: as required, and translation between carriage-return and new-line.
        !           257: It is possible to use one of several terminal handling modules.
        !           258: The standard one provides services like those of the Seventh
        !           259: Edition system;
        !           260: |reference(v7manual)
        !           261: another resembles the Berkeley ``new tty'' driver.
        !           262: |reference(bsdmanual)
        !           263: .1C
        !           264: .KF
        !           265: .PS
        !           266: arrow "user" "write"
        !           267: move up boxht/2
        !           268: line right boxwid/2 then down boxht then left boxwid/2
        !           269: move by (boxwid/2, boxht/2)
        !           270: arrow right
        !           271: box "tty out"; arrow
        !           272: line up boxht/2
        !           273: line right boxwid/2 
        !           274: move down boxht
        !           275: line left boxwid/2 then up boxht/2
        !           276: move right boxwid/2
        !           277: arrow "device" "out"
        !           278: move down boxht
        !           279: arrow left "device" "in"
        !           280: move down boxht/2
        !           281: line left boxwid/2 then up boxht
        !           282: move down boxht/2
        !           283: arrow left
        !           284: box "tty in"; arrow
        !           285: move up boxht/2
        !           286: line down boxht then left boxwid/2
        !           287: move up boxht/2
        !           288: arrow left "user" "read"
        !           289: .PE
        !           290: .sp .5
        !           291: .ce
        !           292: Figure 2.  Configuration for normal terminal attachment.
        !           293: .sp
        !           294: .KE
        !           295: .2C
        !           296: .PP
        !           297: The processing modules in a stream are thought of as a stack whose
        !           298: top (shown here on the left) is next to the user program.
        !           299: Thus, to install the terminal processing module after opening
        !           300: a terminal device, the program that makes
        !           301: such connections executes a ``push'' I/O control call
        !           302: naming the relevant stream and the desired processing module.
        !           303: Other primitives pop a module from the stack
        !           304: and determine the name of the topmost module.
        !           305: .PP
        !           306: Most of the machines using the version of the operating system 
        !           307: described here are connected to a network based on the
        !           308: Datakit\(tm packet switch.
        !           309: |reference(datakit asynchronous)
        !           310: Although there is a variety of host interfaces to the network,
        !           311: most of ours are primitive, and require network protocols
        !           312: to be conducted by the host machine, rather than by a front-end
        !           313: processor.
        !           314: Therefore, when terminals are connected to a host through the network,
        !           315: a setup like that shown in Fig. 3 is used; the terminal processing
        !           316: module is stacked on the network protocol module.
        !           317: Again, there is a choice of protocol modules,
        !           318: both a current standard and an older protocol that is being phased
        !           319: out.
        !           320: .1C
        !           321: .KF
        !           322: .PS
        !           323: arrow "user" "write"
        !           324: move up boxht/2
        !           325: line right boxwid/2 then down boxht then left boxwid/2
        !           326: move by (boxwid/2, boxht/2)
        !           327: arrow right
        !           328: box "tty out"; arrow
        !           329: box "proto out"; arrow
        !           330: line up boxht/2
        !           331: line right boxwid/2 
        !           332: move down boxht
        !           333: line left boxwid/2 then up boxht/2
        !           334: move right boxwid/2
        !           335: arrow "device" "out"
        !           336: move down boxht
        !           337: arrow left "device" "in"
        !           338: move down boxht/2
        !           339: line left boxwid/2 then up boxht
        !           340: move down boxht/2
        !           341: arrow left
        !           342: box "proto in"; arrow
        !           343: box "tty in"; arrow
        !           344: move up boxht/2
        !           345: line down boxht then left boxwid/2
        !           346: move up boxht/2
        !           347: arrow left "user" "read"
        !           348: .PE
        !           349: .sp .5
        !           350: .ce
        !           351: Figure 3.  Configuration for network terminals.
        !           352: .sp
        !           353: .KE
        !           354: .2C
        !           355: .PP
        !           356: A common fourth configuration (not illustrated) is used when
        !           357: the network is used for file transfers or other purposes
        !           358: when terminal processing is not needed.
        !           359: It simply omits the ``tty'' module and uses only the protocol module.
        !           360: Some of our machines, on the other hand, have front-end processors programmed
        !           361: to conduct standard network protocol.
        !           362: Here a connection for remote file transfer will resemble
        !           363: that of Fig. 1, because the protocol is handled outside the operating
        !           364: system; likewise network terminal connections via the front end will be
        !           365: handled as shown in Fig. 2.
        !           366: .NH
        !           367: Messages
        !           368: .PP
        !           369: Most of the messages between modules contain data.
        !           370: The allocator that dispenses message blocks
        !           371: takes an argument specifying the smallest block its caller
        !           372: is willing to accept.
        !           373: The current allocator maintains an inventory of blocks
        !           374: 4, 16, 64, and 1024 characters long.
        !           375: Modules that allocate blocks choose a size by balancing
        !           376: space loss in block linkage overhead against unused space in the block.
        !           377: For example, the top-level
        !           378: .I write
        !           379: routine requests either 64- or 1024-character blocks,
        !           380: because such calls
        !           381: usually transmit many characters;
        !           382: the network input routine allocates 16-byte blocks because
        !           383: data arrives in packets of that size.
        !           384: The smallest blocks are used only to carry arguments to the
        !           385: control messages discussed below.
        !           386: .PP
        !           387: Besides data blocks, there are also several kinds of control messages.
        !           388: The following messages are queued along with data messages,
        !           389: in order to ensure that their effect occurs at the appropriate time.
        !           390: .IP BREAK 10
        !           391: is generated by a terminal device on detection of a line
        !           392: break signal.
        !           393: The standard terminal input processor turns this message into an interrupt
        !           394: request.
        !           395: It may also be sent to 
        !           396: a terminal device driver to cause it to generate a break on the output line.
        !           397: .IP HANGUP
        !           398: is generated by a device when its remote connection drops.
        !           399: When the message arrives at the top level it is turned into an interrupt
        !           400: to the process, and it also marks the stream so that
        !           401: further attempts to use it return errors.
        !           402: .IP DELIM
        !           403: is a delimiter in the data.
        !           404: Most of the stream I/O system is prepared to provide true streams,
        !           405: in which record boundaries are insignificant, but there are
        !           406: various situations in which it is desirable to delimit the data.
        !           407: For example, terminal input is read a line at a time;
        !           408: .SM DELIM
        !           409: is generated by the terminal input processor to demarcate lines.
        !           410: .IP DELAY
        !           411: tells terminal drivers to generate a real-time delay
        !           412: on output; it allows time for slow terminals react to characters previously
        !           413: sent.
        !           414: .IP IOCTL
        !           415: messages are generated by users'
        !           416: .I ioctl
        !           417: system calls.
        !           418: The relevant parameters are gathered at the top level,
        !           419: and if the request is not understood there, it and its parameters
        !           420: are composed into a message and sent down the stream.
        !           421: The first module that understands the particular
        !           422: request acts on it and returns a positive acknowledgement.
        !           423: Intermediate modules that do not recognize a particular
        !           424: .SM IOCTL
        !           425: request pass it on;
        !           426: stream-end modules return a negative acknowledgement.
        !           427: The top-level routine waits for the acknowledgement, and returns
        !           428: any information it carries to the user.
        !           429: .LP
        !           430: Other control messages are asynchronous and jump over
        !           431: queued data and non-priority control messages.
        !           432: .IP IOCACK 10
        !           433: .IP IOCNAK
        !           434: acknowledge
        !           435: .SM IOCTL
        !           436: messages.
        !           437: The device end of a stream must respond with one of these
        !           438: messages;
        !           439: the top level will eventually time out if no response is received.
        !           440: .IP SIGNAL
        !           441: messages are generated by the terminal processing module
        !           442: and cause the top level to generate process signals such
        !           443: as
        !           444: .I quit
        !           445: and
        !           446: .I interrupt.
        !           447: .IP FLUSH
        !           448: messages are used to throw away data from input and output queues
        !           449: after a signal or on request of the user.
        !           450: .IP STOP
        !           451: .IP START
        !           452: messages are used by the terminal processor to halt and restart
        !           453: output by a device, for example to implement the traditional
        !           454: control-S/control-Q (X-on/X-off) flow control mechanism.
        !           455: .1C
        !           456: .KF top
        !           457: .P1
        !           458: struct queue {
        !           459:        int     flag;           /* flag bits */
        !           460:        void    (*putp)();      /* put procedure */
        !           461:        void    (*servp)();     /* service procedure */
        !           462:        struct  queue *next;    /* next queue downstream */
        !           463:        struct  block *first;   /* first data block on queue */
        !           464:        struct  block *last;    /* last data block on queue */
        !           465:        int     hiwater;        /* max characters on queue */
        !           466:        int     lowater;        /* wakeup point as queue drains */
        !           467:        int     count;          /* characters now on queue */
        !           468:        void    *ptr;           /* pointer to private storage */
        !           469: };
        !           470: .P2
        !           471: .ce
        !           472: Figure 4. The queue data structure
        !           473: .KE
        !           474: .2C
        !           475: .NH
        !           476: Queue Mechanisms and Interfaces
        !           477: .PP
        !           478: Associated with each direction of a full-duplex stream module
        !           479: is a queue data structure (somewhat simplified
        !           480: for exposition) shown in Figure 4.
        !           481: The
        !           482: .CW flag
        !           483: word contains several bits used by low-level routines to control
        !           484: scheduling: they show whether the downstream module wishes to read data,
        !           485: or the upstream module wishes to write, or the queue is already
        !           486: enabled.
        !           487: One bit is examined by the upstream module; it tells whether this
        !           488: queue is full.
        !           489: .PP
        !           490: The
        !           491: .CW first
        !           492: and
        !           493: .CW last
        !           494: members point to the head and tail of a singly-linked list of
        !           495: data and control blocks that form the queue proper;
        !           496: .CW hiwater
        !           497: and
        !           498: .CW lowater
        !           499: are initialized when the queue is created, and when compared against
        !           500: .CW count,
        !           501: the current size of the queue, determine whether the queue is full
        !           502: and whether it has emptied sufficiently to enable a blocked writer.
        !           503: .PP
        !           504: The
        !           505: .CW ptr
        !           506: member stores an untyped pointer that may be used by the queue
        !           507: module to keep track of the location of storage private
        !           508: to itself.
        !           509: For example,
        !           510: each instantiation of the terminal processing module
        !           511: maintains
        !           512: a structure containing various mode bits and special characters;
        !           513: it stores a pointer to this structure here.
        !           514: The type of
        !           515: .CW ptr
        !           516: is artificial.
        !           517: It should be a union of pointers to each possible module
        !           518: state structure.
        !           519: .1C
        !           520: .KF bottom
        !           521: .P1
        !           522: service(q)
        !           523: struct queue *q
        !           524: .sp .5
        !           525:        while (q is not empty and q->next is not full) {
        !           526:                get a block from q
        !           527:                process message block
        !           528:                call q->next->putp to dispose of
        !           529:                     new or transformed block
        !           530:        }
        !           531: .P2
        !           532: .ce
        !           533: Figure 5. Typical service routine
        !           534: .KE
        !           535: .2C
        !           536: .PP
        !           537: Stream processing modules are written in one of two general
        !           538: styles.
        !           539: In the simpler kind, the queue module acts nearly as a classical
        !           540: coroutine.
        !           541: When it is instantiated, it sets its put procedure
        !           542: .CW putp
        !           543: to a system-supplied default routine, and supplies a service
        !           544: procedure
        !           545: .CW servp .
        !           546: Its upstream module disposes of blocks by calling this
        !           547: module's
        !           548: .CW putp
        !           549: routine, which places the block on this module's queue (by
        !           550: manipulating the
        !           551: .CW first
        !           552: and
        !           553: .CW last
        !           554: pointers.)
        !           555: The standard put procedure also
        !           556: enables the current module;
        !           557: a short time later the current module's
        !           558: service procedure
        !           559: .CW servp
        !           560: is called by the scheduler.
        !           561: Figure 5 shows the pseudo-code,
        !           562: for a typical service routine.
        !           563: This mechanism is appropriate in cases in which messages can be
        !           564: processed independently of each other.
        !           565: For example, it is used by the terminal output module.
        !           566: All the scheduling details are taken care of by standard routines.
        !           567: .PP
        !           568: More complicated modules need finer control over scheduling.
        !           569: A good example is terminal input.
        !           570: Here the device module upstream produces characters, usually one at a time,
        !           571: that must be gathered into a line to allow for character erase and kill
        !           572: processing.
        !           573: Therefore the stream input module provides a put procedure to be called
        !           574: by the device driver or other module downstream from it;
        !           575: Figure 6 shows an outline
        !           576: of this routine and its accompanying service procedure.
        !           577: The put procedure generates the echo characters
        !           578: as promptly as possible;
        !           579: when the terminal module is attached to a
        !           580: device handler, they are created during the input interrupt
        !           581: from the device, because the put procedure is called as a subroutine
        !           582: of the handler.
        !           583: On the other hand, line-gathering and erase and kill processing,
        !           584: which can be lengthy, are done during the service procedure
        !           585: at lower priority.
        !           586: .1C
        !           587: .KF top
        !           588: .P1
        !           589: putproc(q, bp)
        !           590: struct queue *q; struct block *bp
        !           591: .sp .5
        !           592:        put bp on q
        !           593:        echo characters in bp's data
        !           594:        if (bp's data contains new-line or carriage return)
        !           595:                enable q
        !           596: .sp
        !           597: service(q)
        !           598: struct queue *q
        !           599: .sp .5
        !           600:        take data from q until new-line or carriage return,
        !           601:           processing erase and kill characters
        !           602:        call q->next->putp to hand line to upstream queue
        !           603:        call q->next->putp with DELIM message
        !           604: .P2
        !           605: .ce
        !           606: Figure 6. A typical put routine
        !           607: .SP
        !           608: .KE
        !           609: .2C
        !           610: .NH
        !           611: Connection with the Rest of the System
        !           612: .PP
        !           613: Although all the drivers for terminal and network devices, and all
        !           614: protocol handlers, were rewritten,
        !           615: only minor changes were required elsewhere in the system.
        !           616: Character devices and a character device switch,
        !           617: as described by Thompson|reference(thompson implementation bstj),
        !           618: are still present.
        !           619: A pointer in the character device switch structure, if null,
        !           620: causes the system to treat the device as always;
        !           621: this is used for raw disk and tape, for example.
        !           622: If not null, it
        !           623: points to initialization information for the stream device;
        !           624: when a stream device is opened, the queue structure shown in Fig. 1
        !           625: is created, using this information,
        !           626: and a pointer to the structure naming the stream is saved (in
        !           627: the ``inode table;''|reference(thompson implementation  bstj).
        !           628: .PP
        !           629: Subsequently, when the user process makes
        !           630: .I read ,
        !           631: .I write ,
        !           632: .I ioctl ,
        !           633: or
        !           634: .I close
        !           635: calls, presence of a non-null stream pointer directs the
        !           636: system to use a set of stream routines to generate and receive
        !           637: queue messages; these are the ``top-level routines'' referred
        !           638: to previously.
        !           639: .PP
        !           640: Only a few changes in user-level code are necessary,
        !           641: most because opening a terminal puts it in the
        !           642: ``very raw'' mode shown in Fig. 1.
        !           643: In order to install the terminal-processing handler,
        !           644: it is necessary for programs such as
        !           645: .I init
        !           646: to execute the appropriate
        !           647: .I ioctl
        !           648: call.
        !           649: .NH
        !           650: Interprocess Communication
        !           651: .PP
        !           652: As previously described, the stream I/O system constitutes a flexible
        !           653: communication path between user processes and devices.
        !           654: With a small addition, it also provides a mechanism for interprocess
        !           655: communication.
        !           656: A special device, the ``pseudo-terminal'' or
        !           657: .SM PT ,
        !           658: connects processes.
        !           659: .SM PT
        !           660: files come in even-odd pairs;
        !           661: data written on the odd member of the pair
        !           662: appears as input for the even member, and vice versa.
        !           663: The idea is not new;
        !           664: it appears in Tenex|reference(bobrow burchfiel tenex)
        !           665: and its successors, for example.
        !           666: It is analogous to pipes,
        !           667: and especially to named pipes.|reference(system iii manual)
        !           668: .SM PT
        !           669: files differ from traditional
        !           670: pipes in two ways:
        !           671: they are full-duplex,
        !           672: and control information passes through them as well as data.
        !           673: They differ from the usual pseudo-terminal files
        !           674: |reference(bsdmanual)
        !           675: by not having any of the usual
        !           676: terminal processing mechanisms inherently attached to them;
        !           677: they are pure transmitters of control and data messages.
        !           678: .SM PT
        !           679: files are adequate for setting up
        !           680: a reasonably general mechanism for explicit process communication,
        !           681: but by themselves are not especially interesting.*
        !           682: .FS
        !           683: *More recent research systems (Ninth and Tenth editions) drop
        !           684: .SM PT
        !           685: files altogether.
        !           686: They use pipes where the name of the connection
        !           687: is not needed, and `mounted streams' where a name is needed.
        !           688: See |reference(latest presotto ipc).
        !           689: .FE
        !           690: .1C
        !           691: .KF bottom
        !           692: .PS
        !           693: box invis height 2*boxht "device" "process"
        !           694: move up boxht/2
        !           695: arrow right linewid/2
        !           696: move up boxht/2
        !           697: line right boxwid/2 then down boxht then left boxwid/2
        !           698: move by (boxwid/2, boxht/2)
        !           699: arrow right
        !           700: box "mesg"; arrow
        !           701: line up boxht/2
        !           702: line right boxwid/2 
        !           703: move down boxht
        !           704: line invis left boxwid/4 "pt"
        !           705: line left boxwid/4 then up boxht/2
        !           706: move right boxwid/2
        !           707: spline -> right boxwid then down 4*boxht then left boxwid
        !           708: move down boxht/2
        !           709: line left boxwid/2 then up boxht
        !           710: line right boxwid/4
        !           711: line invis right boxwid/4 "pt"
        !           712: move by (-boxwid/2, -boxht/2)
        !           713: arrow left
        !           714: box "tty in"; arrow
        !           715: move up boxht/2
        !           716: line down boxht then left boxwid/2
        !           717: move up boxht/2
        !           718: arrow left linewid/2
        !           719: move up boxht/2
        !           720: left
        !           721: box invis height boxht*2 "user" "process"
        !           722: move up boxht/2
        !           723: move right boxwid
        !           724: arrow right linewid/2
        !           725: move up boxht/2
        !           726: line right boxwid/2 then down boxht then left boxwid/2
        !           727: move by (boxwid/2, boxht/2)
        !           728: arrow right
        !           729: box "tty out"
        !           730: arrow right
        !           731: move down boxht/2
        !           732: line up boxht then right boxwid/2
        !           733: move down boxht/2
        !           734: spline ->  right boxwid/2 then up 2*boxht then left boxwid/2
        !           735: move down boxht/2
        !           736: line left boxwid/2 then up boxht
        !           737: move down boxht/2
        !           738: arrow left
        !           739: box "mesg"
        !           740: arrow left
        !           741: move up boxht/2
        !           742: line down boxht then left boxwid/2
        !           743: move up boxht/2
        !           744: arrow left linewid/2
        !           745: .PE
        !           746: .sp .5
        !           747: .ce
        !           748: Figure 7.  Configuration for device simulator.
        !           749: .sp
        !           750: .KE
        !           751: .2C
        !           752: .PP
        !           753: A special
        !           754: .I message
        !           755: module provides more intriguing possibilities.
        !           756: In one direction,
        !           757: the message processor takes control and data messages, such as those discussed
        !           758: above, and transforms them into data blocks starting with a header giving
        !           759: the message type, and followed by the message content.
        !           760: In the other direction, it parses similarly-structured data messages
        !           761: and creates the corresponding control blocks.
        !           762: Figure 7 shows a configuration in which
        !           763: a user process communicates through the terminal module, a
        !           764: .SM PT
        !           765: file pair, and the message module with another user-level process
        !           766: that simulates a device driver.
        !           767: Because
        !           768: .SM PT
        !           769: files are transparent, and the message module
        !           770: maps bijectively between device-process data and stream control messages,
        !           771: the device simulator may be completely faithful up to details
        !           772: of timing.
        !           773: In particular, user's
        !           774: .I ioctl
        !           775: requests are sent to the device process and are handled by it,
        !           776: even if they are not understood by the operating system.
        !           777: .PP
        !           778: The usefulness of this setup is not so much to simulate new
        !           779: devices, but to provide
        !           780: ways for one program to control the environment
        !           781: of another.
        !           782: Pike|reference(blit bstj)
        !           783: shows how these mechanisms are used to create
        !           784: multiple virtual terminals on one physical terminal.
        !           785: In another application, inter-machine connections in which
        !           786: a user on one computer logs into another make use of the message module.
        !           787: Here the
        !           788: .I ioctl
        !           789: requests generated by programs on the remote machine
        !           790: are translated by this module into data messages that can be sent over
        !           791: the network.
        !           792: The local callout program translates them back into terminal control commands.
        !           793: ......
        !           794: .NH
        !           795: Evaluation
        !           796: .PP
        !           797: My intent in rewriting the character I/O system
        !           798: was to improve its structure by separating functions
        !           799: that had been intertwined, and by allowing independent modules
        !           800: to be connected dynamically across well-defined interfaces.
        !           801: I also wanted to make the system faster and smaller.
        !           802: The most difficult part of the project was the design of the interface.
        !           803: It was guided by these decisions:
        !           804: .IP 1)
        !           805: It seemed to be necessary for efficiency
        !           806: that the objects passed between modules be references to blocks of data.
        !           807: The most important consequences of this principle, and those
        !           808: that proved deciding, are that data need not be copied as it passes
        !           809: across a module interface, and that many characters can be handled
        !           810: during a single intermodule transmission.
        !           811: Another effect, undesirable but accepted, is that each module
        !           812: must be prepared to handle discrete chunks of data of unpredictable size.
        !           813: For example, a protocol that expects records containing (say)
        !           814: an 8-byte header must be prepared to paste together smaller data blocks
        !           815: and split a block containing both a header and following data.
        !           816: A related, although not necessarily consequent, decision was to make
        !           817: the code assume that the data is addressable. 
        !           818: .IP 2)
        !           819: I decided, with regret, that each processing module
        !           820: could not act as an independent process with its own call record.
        !           821: The numbers seemed against it: on large systems it is necessary
        !           822: to allow for as many as 1000 queues, and I saw no good way
        !           823: to run this many processes without consuming inordinate amounts
        !           824: of storage.
        !           825: As a result,
        !           826: stream server procedures are not allowed to block awaiting data,
        !           827: but instead must return after saving necessary status information
        !           828: explicitly.
        !           829: The contortions required in the code are seldom serious in practice,
        !           830: but the beauty of the scheme would increase if servers
        !           831: could be written as a simple read-write loop in the true coroutine style.
        !           832: .IP 3)
        !           833: The characteristic feature of the design\-the server and put
        !           834: procedures\-was the most difficult to work out.
        !           835: I began with a belief that the intermodule interface
        !           836: should be identical in the read and write directions.
        !           837: Next, I observed that a pure call model (put procedure only)
        !           838: would not work;
        !           839: queueing would be necessary at some point.
        !           840: For example, if the
        !           841: .I write
        !           842: system entry called through the terminal processing module to the device
        !           843: driver, the driver would need to queue characters internally lest output
        !           844: be completely synchronous.
        !           845: On the other hand, a pure queueing model (service procedure only;
        !           846: upstream modules always place their data in an input queue)
        !           847: also appeared impractical.
        !           848: As discussed above, a module (for example terminal input)
        !           849: must often be activated at times that depend on its input data.
        !           850: .PP
        !           851: After considerable churning of details, the model presented here emerged.
        !           852: In general its performance by various measures lives up to hopes.
        !           853: .PP
        !           854: The improvement in modularity is hard to measure,
        !           855: but seems real;
        !           856: for example, the number of
        !           857: included header
        !           858: files in stream modules drops to about one half of those required
        !           859: by similar routines in the base system
        !           860: (4.1 BSD).
        !           861: Certainly stream modules may be composed more freely
        !           862: than were the ``line disciplines'' of older systems.
        !           863: .PP
        !           864: The program text size of the version of the operating 
        !           865: system described here is about 106 kilobytes on the VAX;
        !           866: the base system was about 130KB.
        !           867: The reduction was achieved by rewriting the various device drivers
        !           868: and protocols and eliminating the Seventh Edition
        !           869: multiplexed files
        !           870: |reference(v7manual)
        !           871: , most (though not all)
        !           872: of whose functions are subsumed by other mechanisms.
        !           873: On the other hand, the data space has increased.
        !           874: On a VAX 11/750 configured for 32 users
        !           875: about 32KB are used for storage of the structures for streams,
        !           876: queues, and blocks.
        !           877: The traditional character lists seem to require less;
        !           878: similar systems from Berkeley and AT&T use between 14 and 19KB.
        !           879: The tradeoff of program for data seems desirable.
        !           880: .PP
        !           881: Proper time comparisons
        !           882: have not been made,
        !           883: because of the difficulty of finding a comparable configuration.
        !           884: On a VAX 11/750,
        !           885: printing a large file on a directly-connected terminal
        !           886: consumes 346 microseconds per character using the system described here;
        !           887: this is about 10 per cent slower than the base system.
        !           888: On the other hand, that system's per-character interrupt routine is
        !           889: coded in assembly language, and the rest of its terminal handler
        !           890: is replete with nonportable
        !           891: interpolated assembly code;
        !           892: the current system is written completely in C.
        !           893: Printing the same file on a terminal connected through a primitive
        !           894: network interface requires 136 microseconds per character,
        !           895: half as much as the older network routines.
        !           896: Pike
        !           897: |reference(blit bstj)
        !           898: observes that among the three implementations of Blit connection software,
        !           899: the one based on the stream system is the only one that can download programs
        !           900: at anything approaching line speed through a 19.2 Kbps connection.
        !           901: In general I conclude that the new organization
        !           902: never slows comparable tasks much, and that considerable speed improvements
        !           903: are sometimes possible.
        !           904: .PP
        !           905: Although the new organization performs well, it has several peculiarities
        !           906: and limitations.
        !           907: Some of them seem inherent, some are fixable,
        !           908: and some are the subject of current work.
        !           909: .PP
        !           910: I/O control calls turn into messages that require answers
        !           911: before a result can be returned to the user.
        !           912: Sometimes the message ultimately goes to another user-level process
        !           913: that may reply tardily or never.
        !           914: The stream is write-locked until the reply returns, in order
        !           915: to eliminate the need to determine which process gets which reply.
        !           916: A timeout breaks the lock,
        !           917: so there is an unjustified error return if a reply
        !           918: is late, and a long lockup period if one is lost.
        !           919: The problem can be ameliorated by working harder on it, but
        !           920: it typifies the difficulties that turn up when direct calls are replaced
        !           921: by message-passing schemes.
        !           922: .PP
        !           923: Several oddities appear because time spent in server routines
        !           924: cannot be assigned to any particular user or process.
        !           925: It is impossible, for example, for devices to support privileged
        !           926: .I ioctl
        !           927: calls, because the device has no idea who generated the message.
        !           928: Accounting and scheduling become less accurate;
        !           929: a short census of several systems showed that between 4 and 8 per cent
        !           930: of non-idle CPU time was being spent in server routines.
        !           931: Finally, the anonymity of server processing most certainly makes
        !           932: it more difficult to measure the performance of the new I/O system.
        !           933: .PP
        !           934: In its current form the stream I/O system is purely data-driven.
        !           935: That is, data is presented by a user's
        !           936: .I write
        !           937: call, and passes through to the device; conversely, data appears unbidden
        !           938: from a device and passes to the top level, where it is picked up by
        !           939: .I read
        !           940: calls.
        !           941: Wherever possible flow control throttles down fast
        !           942: generators of data, but nowhere except at the consumer end of a stream
        !           943: is there knowledge of precisely how much data is desired.
        !           944: Consider a command to execute possibly interactive program on another
        !           945: machine connected by a stream.
        !           946: The simplest such command sets up the connection
        !           947: and invokes the remote program, and then copies characters from its
        !           948: own standard input to the stream, and from the stream to its standard
        !           949: output.
        !           950: The scheme is adequate in practice,
        !           951: but breaks when the user types more than the remote program expects.
        !           952: For example, if the remote program reads no input at all,
        !           953: any typed-ahead characters are sent to the remote system and lost.
        !           954: This demonstrates a problem,
        !           955: but I know of no solution inside the stream I/O mechanism itself;
        !           956: other ideas will have to be applied.
        !           957: .PP
        !           958: Streams are linear connections; by themselves, they support no notion
        !           959: of multiplexing, fan-in or fan-out.
        !           960: Except at the ends of a stream, each invocation of a module has
        !           961: a unique ``next'' and ``previous'' module.
        !           962: Two locally-important applications
        !           963: of streams testify to the importance of multiplexing:
        !           964: Blit terminal connections
        !           965: |reference(blit bstj)
        !           966: , where the multiplexing is done well, though at some performance cost,
        !           967: by a user program,
        !           968: and remote execution of commands over a network,
        !           969: where it is desired, but not now easy,
        !           970: to separate the standard output from error output.
        !           971: It seems likely that a general multiplexing mechanism
        !           972: could help in both cases, but again, I do not yet know how to
        !           973: design it.*
        !           974: .FS
        !           975: *Subsequent Research versions of the system still do not
        !           976: have a general stream-multiplexing scheme built into the
        !           977: kernel, while the System V streams implementations do.
        !           978: We have not found any applications that suffer from the
        !           979: lack of stream-multiplexing.
        !           980: .FE
        !           981: .PP
        !           982: Although the current design provides elegant means for controlling
        !           983: the semantics of communication channels already opened, it lacks
        !           984: general ways of establishing channels between processes.
        !           985: The
        !           986: .SM PT
        !           987: files described above are just fine for
        !           988: Blit layers,
        !           989: and work adequately for handling a few administrator-controlled
        !           990: client-server relationships.
        !           991: (Yes, we have multi-machine mazewar.)
        !           992: Nevertheless, better naming mechanisms are called for.\(dg
        !           993: .FS
        !           994: \(dgSee |reference(latest presotto ipc)
        !           995: for our current ideas.
        !           996: .FE
        !           997: .PP
        !           998: In spite of these limitations, the stream I/O system works well.
        !           999: Its aim was to improve design rather than to add features,
        !          1000: in the belief that with proper design, the features come cheaply.
        !          1001: This approach is arduous, but continues to succeed.
        !          1002: .NH
        !          1003: References
        !          1004: .LP
        !          1005: |reference_placement

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.