Within the filesystem server library, a number of different abstractions and mechanisms are employed to provide access to filesystem objects. An overview of these abstractions is presented below.
Firstly, an Opener must be obtained from a Filesystem, this configuring the Opener for a particular user identity.
An Opener requests a Resource from a ResourceRegistry, each Resource acting as a Pager and providing the actual access mechanism to file content for a particular program. Since many programs may access the same file simultaneously, a Provider object represents a file opened in the system, retaining a PageMapper that coordinates the collective access to the file (see the paging documentation for more information).
A ProviderRegistry maintains the record of opened files, yielding a new Provider for an unopened file or an existing Provider for a file that is already open. The ProviderRegistry monitors the usage of files, discarding any Provider no longer in use.
Opening a file involves the identification of the filesystem object involved, the acquisition of a Provider representing the file, and the creation of a Resource to deliver file content to the requesting program.
Where a provider does not already exist, with the file not having been opened already, some extra interactions occur:
The Resource obtained to deliver file content to a client will be served by a distinct thread through a dedicated communications endpoint, leaving the Opener available to satisfy other requests. This new thread will remain active while a file remains open, being terminated when the file is closed and the communications endpoint released by the client.
Within the filesystem access architecture, users of files may act in ways that may notify other users of the same file. To support such notifications, an interface is provided for filesystem clients to subscribe and unsubscribe to notifications. The notifications themselves occur when files are opened, modified and closed. Pipes also generate notifications when a pipe reader makes more space available for the writer.
An example of a notification scenario is given below:
Notifications provide the basis for blocking reading and writing operations, with pipe endpoints depending on such blocking for efficient use of pipes. The interactions involved when transferring data through pipes are depicted below.
Files are closed when client programs release their references to the resource capabilities used to access files. The mechanism by which this is done involves the registration of an IRQ object that is bound to the same thread as the one used by resources to service file access requests. This IRQ object is associated with the IPC gate through which such requests occur, and the IRQ object is configured to deliver an interrupt message when the final reference to the IPC gate has been released.
When a client program terminates, its references to capabilities should be released, and this should cause a reference counter associated with the IPC gate to decrement. Upon the loss of the final reference and the delivery of the interrupt, the server framework will call a close method on the Resource object concerned, this implementing the Accountable interface.
As a consequence of closing a file, an unsubscribe operation performed on the Provider should cause any Notifier objects associated with the Resource object to be released. Meanwhile, the ResourceServer will discard the Resource object before the server thread terminates.
Unix filesystem semantics typically allow a file to be "unlinked" while still being accessed by a program, with accesses still being directed to the file, but with the file being removed from a directory and potentially being replaced by another file. To support this, a mechanism is provided to defer any removal of an open file.