biagioni@unc.UUCP (Edoardo Biagioni) (11/06/86)
(***************************************************************************) (*** ***) (*** ***) (*** O S S I ***) (*** ========== ***) (*** ***) (**) DEFINITION MODULE SIBlockIO; (**) (*** =========================== ***) (*** ***) (*** This module allows block access to mass storage devices, ***) (*** thus enabling the user to implement his/her own input/output. ***) (*** The module only provides primitive, unsafe low level ***) (*** I/O operations. ***) (*** ***) (***---------------------------------------------------------------------***) (*** ***) (*** Hardware: VAX-11 ***) (*** Operating System: UNIX BSD 4.2 ***) (*** Compiler: independent ***) (*** ***) (*** Version: 3.0 ***) (*** Implemented: see copyright ***) (*** Date: 1986-03-11 ***) (*** ***) (***---------------------------------------------------------------------***) (*** ***) (*** Copyright 1984, 1985, 1986 by ***) (*** E. S. Biagioni ***) (*** G. Heiser ***) (*** K. Hinrichs ***) (*** C. Muller ***) (*** ***) (*** Institut fuer Informatik ***) (*** ETH Zuerich ***) (*** CH 8092 Zuerich ***) (*** Switzerland ***) (*** ***) (*** Department of Computer Science ***) (*** University of North Carolina ***) (*** Chapel Hill, North Carolina 27514 ***) (*** U.S.A. ***) (*** ***) (*** Permission to copy without fee all of this material is granted ***) (*** provided that the copies are not made or distributed for direct ***) (*** commercial advantage, that this OSSI copyright notice is ***) (*** included in the copy, that the module is not modified in any way ***) (*** except where necessary for compilation on a particular system, ***) (*** and that the module is always distributed in its original form. ***) (*** Distribution of this module in a modified form without including ***) (*** the original version is a violation of this copyright notice. ***) (*** ***) (***---------------------------------------------------------------------***) (*** ***) (*** Updates: ***) (*** ***) (*** ***) (***************************************************************************) (* Storage is allocated in pieces called "segments", each segment consisting of an integral number of blocks. Blocks are of a fixed size, namely "BlockLength" bytes. The implementation of the module should attempt to allocate a contiguous section of memory for each segment; in any case it must give the user the illusion of contiguous segments. Segments are located on devices. The meaning of "device", and the syntax of a DeviceName is system dependent and will in general conform with system conventions. A device is made accessible by a call to OpenDevice. This procedure accepts a string containing the device name and, if successful, returns a DeviceId. The DeviceId is used in all further SIBlockIO operations on that particular device and the segments on the device. A particular device can be opened more than once at the same time, a different DeviceId will be returned by each open operation. If a device is no longer needed, it must be closed by a call to CloseDevice. This call dissasociates the DeviceId from the DeviceName (and the physical device) and guarantees a consistent state of the data stored on the device. After closing a device, the DeviceId must not be used again. W a r n i n g: Devices left open at program termination time are in an undefined state. Some of their data may be corrupted or unaccessible. Each segment on a particular device is uniquely identified by a "SegmentId" which serves as the user's address of the segment. It is the user's sole responsibility to manage his/her segments, that is to remember and properly use their respective ids and sizes. To this purpose the system provides for the usage of "NamedBlocks" in which the user can implement his/her directories. A NamedBlock is a segment with some special properties: 1) a NamedBlock has a name in addition to its id, 2) a NamedBlock is always a segment of length 1 (blocks), 3) a NamedBlock must never be allocated/deallocated using the regular allocation/deallocation procedures CreateSegment/DeleteSegment. An actual implementation may or may not detect violations of this rule. A block name is a character string. It is the user's identification of the NamedBlock (and all the segments managed by means of this block). For a given physical device (characterized by the deviceName), block names must be unique. In order to perform any I/O operation on the NamedBlock, a SegmentId must be attached to the block. This is done by a call to the procedure GetId, after the NamedBlock has been introduced by a call to CreateNamedBlock. The SegmentId is the user's address of the block, used to perform I/O operations by means of the procedures ReadSegment and WriteSegment. "Normal" segments are de/allocated using CreateSegment/DeleteSegment and I/O operations are performed on them using ReadSegment/WriteSegment. As mentioned before, it is the user's responsibility, using a NamedBlock or other means, to manage the ids and sizes of his/her segments. SIBlockIO input/output operations may be implemented asynchronously, provided the underlying system supports asynchronous I/O. That requires some care on the user's side: A call to ReadSegment will only initiate the physical I/O operation. Before accessing the data read into the user-supplied buffer, the user must await completion of the physical read operation. This is done by a call to the Synchronize procedure. Similarly a call to Synchronize must be performed after calling WriteSegment if the user-supplied buffer is to be modified afterwards. Of course, the call to Synchronize does not have to be performed immediately afterwards, the user can do other processing in between, thus possibly avoiding any delays due to waiting for I/O completion. There is an implicit synchronization done by the system before any further I/O operations are performed on the respective device. Hence an explicit call to Synchronize is not required, if a ReadSegment operation follows a WriteSegment call or vice versa, or between successive calls to ReadSegment or WriteSegment using different buffers. The main requirement to any implementation is to minimize the number of physical I/O operations for the procedures ReadSegment and WriteSegment. The procedures CreateSegment, DeleteSegment, CreateNamedBlock and DeleteNamedBlock may be much more expensive in terms of physical I/O operations. *) FROM SISystem IMPORT SIResult, BYTE; EXPORT QUALIFIED DeviceNameLength, BlockNameLength, DeviceIdLength, SegmentIdLength, BlockLength, DefaultDeviceName, DeviceName, DeviceId, BlockName, SegmentId, Block, ConsistencyCheck, OpenDevice, CloseDevice, CreateNamedBlock, DeleteNamedBlock, GetId, CreateSegment, DeleteSegment, ReadSegment, WriteSegment, Synchronize, GetIOCounts; CONST DeviceNameLength = 255; (* VAX *) BlockNameLength = 24; DeviceIdLength = 1; SegmentIdLength = 2; BlockLength = 512; (* VAX *) TYPE DeviceName = ARRAY [0 .. DeviceNameLength - 1] OF CHAR; BlockName = ARRAY [0 .. BlockNameLength - 1] OF CHAR; DeviceId = ARRAY [0 .. DeviceIdLength - 1] OF BYTE; SegmentId = ARRAY [0 .. SegmentIdLength - 1] OF BYTE; Block = ARRAY [0 .. BlockLength - 1] OF BYTE; CONST DefaultDeviceName = "~/.BlockIO"; (* Unix *) (* The meaning of 'result' in the following procedures is machine-dependent, but a successfull operation always returns result=SIDone . *) PROCEDURE ConsistencyCheck (VAR result: SIResult); (* checks the external directory structure for consistency and for agreement with the internal data structure. If an inconsistency is found, result<>SIDone on return *) PROCEDURE OpenDevice (devicename: DeviceName; VAR device: DeviceId; VAR result: SIResult); (* opens the designated device for usage by SIBlockIO and associates it with the DeviceId returned. If the device does not exist or is not (currently) accessible, result<>SIDone on return *) PROCEDURE CloseDevice (device: DeviceId; VAR result: SIResult); (* dissociates the DeviceId from the corresponding device, making it unaccessible by SIBlockIO. If the DeviceId does not designate an opened device, result<>SIDone on return *) PROCEDURE CreateNamedBlock (device: DeviceId; blockname: BlockName; VAR result: SIResult); (* associates a block on device with the given blockname, for later opening by GetId or deleting by DeleteNamedBlock, or both. If a block with this name exists already, result<>SIDone *) PROCEDURE DeleteNamedBlock (device: DeviceId; blockname: BlockName; VAR result: SIResult); (* deletes the block name so that no block is associated with the name. If no such name was found, result <> SIDone *) PROCEDURE GetId (device: DeviceId; blockname: BlockName; VAR segmentid: SegmentId; VAR result: SIResult); (* returns the id associated with the given block name. If no such name was found, result <> BlockIODone. The segmentid returned should not be used in a call to DeleteSegment *) (* a segment is a set of contiguously allocated blocks. The user is responsible for remembering the size of a segment and for using the same size in each procedure call. Failure to observe this rule may result in serious disk and system errors *) PROCEDURE CreateSegment (device: DeviceId; numberofblocks: CARDINAL; VAR segmentid: SegmentId; VAR result: SIResult); (* Returns an identifier for numberofblocks contiguous free (not allocated) blocks. If no space is available for such a segment, result <> SIDone *) PROCEDURE DeleteSegment (device: DeviceId; numberofblocks: CARDINAL; segmentid: SegmentId; VAR result: SIResult); (* Deletes the block set identified by segmentid. numberofblocks must be the same as was used for the corresponding call to CreateSegment *) PROCEDURE ReadSegment (device: DeviceId; numberofblocks: CARDINAL; segmentid: SegmentId; VAR blocks: ARRAY OF Block; VAR result: SIResult); (* initiates reading numberofblocks blocks identified by segmentid. For correct operation, the following conditions must be satisfied: - numberofblocks <= <numberofblocks declared in CreateSegment> - numberofblocks <= HIGH (blocks) + 1 - the segment to be read must have been written before. *) PROCEDURE WriteSegment (device: DeviceId; numberofblocks: CARDINAL; segmentid: SegmentId; VAR blocks: ARRAY OF Block; VAR result: SIResult); (* initiates writing numberofblocks blocks identified by segmentid. For correct operation, the following conditions must be satisfied: - numberofblocks <= <numberofblocks declared in CreateSegment> - numberofblocks <= HIGH (blocks) + 1 *) PROCEDURE Synchronize (device: DeviceId; VAR result: SIResult); (* awaits completion of any previously initiated I/O operation on the specified device. Returns result = SIDone if the previously initiated I/O operation is completed sucessfully or if there was no I/O operation initiated. Otherwise result # SIDone on return. Note that in this case result will indicate an error occured during the execution of the previously initiated I/O operation. *) PROCEDURE GetIOCounts (device: DeviceId; VAR userReadCount, userWriteCount, systemReadCount, systemWriteCount: CARDINAL; VAR result: SIResult); (* returns the number of physical I/O operations performed on the device. userReadCount, userWriteCount: number of user I/O operations (calls to ReadSegment/WriteSegment) systemReadCount, systemWriteCount: number of I/O operations due to system overhead *) END SIBlockIO.