File systems three ways
- Interface
- System calls
open
, read
, write
, close
- Well-defined, standard (
f*
versions standardized in C)
- Kernel implementation
- Virtual file system
struct vnode
, struct file_operations
, struct file
…
- Not standard—you can make your own designs
- Disk representation (for stable-storage file systems)
- Physical file system
- Subject of last lecture
- Inodes, free block bitmaps, superblocks…
- Well-defined because durable
- Different file systems have different designs
Why focus on the virtual file system?
- Systems software design is an important course subject
- Given constraints (e.g., file system interface, multicore design), how best
to achieve a software goal?
- VFS interfaces are entirely internal to the kernel, so you can experiment
Virtual file system terms
- “Virtual” is overused and ambiguous terminology
- Generally refers to abstraction
- A virtual machine is an abstraction of a single concrete computer (processor + hardware)
- A virtual function in C++ unifies the handling of multiple function implementations
- A virtual file system is an abstraction that unifies the handling of concrete file systems
- Virtual file systems were introduced in SunOS in 1986 (paper)
vnode
was introduced in that paper to refer to a “file system independent inode”
Vnodes in 1986

Virtual file system and physical file system
- Physical file systems introduce many important systems concepts and are
fascinating examples of data structure design
* How can we optimize data access?
- How can we make data durable (survive system crashes)?
- You will deal with one physical file system,
Chickadeefs, starting in pset 4
File operations
- Software design depends on recognizing patterns and classes of behavior
- Which objects always behave similarly?
- Which objects have the same type?
- As software gets more complex, richer patterns arise
- Programming languages offer explicit support for patterns through types
- All objects of a given type have similar behaviors (can be passed to function parameters)
- Advanced programming languages offer relationships between types
- Parameterized types: “vector of
T
”
- Inheritance: “any object of type
S
is also an object of type T
”
S
is a subtype, or derived type, of T
(its supertype or base type)
- VFS is an instance of inheritance!
Meaning of inheritance
- Inheritance and subtyping is one way to organize software
- When to choose inheritance?
- Liskov substitution
principle is
one definition of when inheritance makes sense
- “Let \(\phi(x)\) be a property provable about objects \(x\) of type
T
.
Then \(\phi(y)\) should be true of objects \(y\) of type S
where S
is a subtype of T
.”
- In other words, any invariant that holds on objects of the base type
should also hold on objects of the derived type
- Does not hold in reverse: the behavior of derived objects can be more
specific
- VFS examples?
OS/161 vnode_ops
struct vnode {
int vn_refcount; /* Reference count */
struct spinlock vn_countlock; /* Lock for vn_refcount */
struct fs *vn_fs; /* Filesystem vnode belongs to */
void *vn_data; /* Filesystem-specific data */
const struct vnode_ops *vn_ops; /* Functions on this vnode */
};
struct vnode_ops {
...
int (*vop_eachopen)(struct vnode *object, int flags_from_open);
int (*vop_reclaim)(struct vnode *vnode);
int (*vop_read)(struct vnode *file, struct uio *uio);
int (*vop_write)(struct vnode *file, struct uio *uio);
...
};
const struct vnode_operations sfs_fileops, sfs_dirops, semfs_dirops, semfs_semops;
Linux file_operations
struct file {
...
/* Protects f_ep, f_flags. Must not be taken from IRQ context. */
spinlock_t f_lock;
fmode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
struct inode *f_inode;
const struct file_operations *f_op;
void *private_data;
...
};
struct file_operations {
struct module *owner;
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*release) (struct inode *, struct file *);
...
};
const struct file_operations v9fs_file_operations, v9fs_file_operations_dotl, adfs_file_operations,
ext4_file_operations, omg so many wtf;
Virtual functions
class file {
virtual ssize_t read(char* buf, size_t sz, loff_t* fpos) = 0;
virtual ssize_t write(const char* buf, size_t sz, loff_t* fpos) = 0;
};
- What does the compiler turn this into?
Framing questions
- What types of objects make up your VFS layer?
- What are the names and types of their methods?
- What are the relationships among the objects?
- How is memory for each kind of object allocated and freed?
- What synchronization invariants must VFS
callers obey? For instance, which objects have locks? Is there a lock order
necessary to prevent deadlock?
- What future OS changes might require updates to the VFS layer? Consider
especially support for multithreaded processes.
- Which functions rely for safety on checks made elsewhere in the system?
- Which VFS functions, if any, can block?
Presentations