Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.
190 CHAPTER 4 Enforcing Modularity with Clients and Services a disk write for each remote procedure call to record the response. As explained in Section 6.1.8, disk writes are often a performance bottleneck and much more expensive than a remote procedure call. Although the stateless property of NFS simplifies recovery, it makes it impossible to implement the unix file interface correctly because the unix specification requires maintaining state. Consider again the case where one program deletes a file that another program has open. The unix specification is that the file exists until the sec- ond program closes the file. If the programs run on different clients, NFS cannot adhere to this specification because it would require that the server keep state. It would have to maintain a reference count per file, which would be incremented on an open system call and decremented on a close system call, and persist across server failures. In addition, if a client would not respond to messages, the server would have to wait until the client becomes reachable again to decrement the reference count. Instead, NFS just does the easy but slightly wrong thing: remote procedure calls return an error "stale file handle" if a program on another client deletes a file that the first client has open. NFS does not implement the unix specification faithfully because that simplifies the design of NFS. NFS preserves most of the unix semantics, and only in rarely encoun- tered situations may users see different behavior. In practice, these situations are not a serious problem, and in return NFS gets by with simple recovery. 4.5.3 extending the unix File System to Support NFS To implement NFS as an extension of the unix file system while minimizing the num- ber of changes required to the unix file system, the NFS designers split the file sys- tem program by introducing an interface that provides vnodes, virtual nodes (see Figure 4.12). A vnode is a structure in volatile memory that abstracts whether a file or directory is implemented by a local file system or a remote file system. This design allows many functions in the file system call layer to be implemented in terms of vnodes, without having to worry about whether a file or directory is local or remote. The interface has an additional advantage: a computer can easily support several, dif- ferent local file systems. When a file system call must perform an operation on a file (e.g., reading data), it invokes the corresponding procedure through the vnode interface. The vnode inter- face has procedures for looking up a file name in the contents of a directory vnode, reading from a vnode, writing to a vnode, closing a vnode, and so on. The local file system and NFS support their own implementation of these procedures. By using the vnode interface, most of the code for file descriptor tables, current directory, name lookup, and the like, can be moved from the local file system module into the file system call layer with minimal effort. For example, with a few changes, the procedure pathname _ to _ inode from Section 2.5 can be modified to be pathname _ to _ vnode and be provided by the file system call layer.