The first question that popped to your mind whenever we suggest a solution
approach is why? In particular, whose pain point are we trying to
solve? It turns out that many operating system's subsystems need
persistence. For example file system, it has metadata
such as inodes that say where and how files are actually
stored on the disc. And this data structure, which
is the inode data structure, is a persistent data
structure and while the operating system may manipulate that
data structure by bringing it into main memory for performance
reasons. Finally, it has to put it back on
the disk in a persistent state once permanent changes have
been made to that data structure. And that's the
reason why persistence is very, very important for building several
such subsystems. So, a file system I gave
you as an example. Similarly runtime systems of
many languages may require support for persistent objects
that need to be stored on permanent storage.
And all of these subsystems, what they would do is in order to make it efficient
from the point of view of your processing,
they will have in memory a cache copy of
the persistent data that lives on permanent
storage such as the disk. But, if and
when changes are made to those data structures that are in memory, they have to
be committed back to the permanent storage
so that there is consistency for actions that
are being taken by these subsystems. So how
can we provide persistent support for these subsystems?
Well, one possibility is to make virtual
memory persistent, because if you make the virtual
memory system persistent then all the data
structures that are contained in the virtual memory
become automatically persistent. Now the upshot of
doing that would be that the subsystems do
not have to worry about flushing the
persistent data structures from memory into the disk
because some other layer in the software stack is
going to take care of it if in fact
we make virtual memory persistent. That also means that
if the system were to crash either due to
power failure or due to problems in the software,
meaning software bugs, recovering from such crashes becomes easy
because the data structures are persistent and anything that
was done in the virtual memory of the process
that represents that subsystem is going to
be available in the persistent storage. The question
is then, who will use it? Well,
we are expecting that the subsystem designers will
use such an abstraction if we provide
it. But subsystem designers also care about performance.
And therefore, subsystem designers will use it
only if the abstraction is a performant abstraction.
In other words, it is not enough to come up with an abstraction, but it is
also important to make the abstraction efficient. So
if an abstraction is cheap to use, easy
to understand, and flexible, then subsystem designers will
use it. If it is not, they won't.
So the question is, if we want to
make virtual memory persistent, can we also make
it an efficient implementation of the abstraction
of a persistent virtual memory? Now how can
we make it efficient? Now if we think about it, the virtual address base of a
process, and let's say that this virtual address
space represents a process that belongs to a
particular subsystem. It may have persistent data structures
that are strewn all over this address space.
And if these data structures, persistent data structures,
are written into and if these persistent data structures
are manipulated by the subsystem and if we want
them to be committed to the storage, then it
requires that every time we touch a persistent data
structure in-memory version of it and modify it, the
on-disk version of that same data structure needs to
be updated. Now what that means is if these
data structures are spread out all over the
virtual address space, that could result in many
I/O operations, first of all. Second, it could
also mean that we may be writing, to
different portions of the disk. And both of
these are bad news, because disk by it's
very nature, it's electromechanical in nature. And, disk
is electromechanical in nature. And as a result there
are latencies associated with the disc, namely,
seek latency and rotational latency. And if the
writing that we are doing to virtual
memory, which is backed up by the persistent
disk, is all over the disk, then it's going to result in many random writes
to the storage system. And random writes are
going to be resulting in very poor performance.
So instead of doing this, what we would like to do is have what we will
call a log segment. And the idea is very similar to the log structure file
system that we have seen earlier when we discussed XFS. The idea is that, when
you're making changes to the virtual memory and
you know what portions of it are persistent,
every time you write to a persistent data structure
that is in memory, then you write a log
record that corresponds to the change that you made
to the persistent data structure. So this is the
change you made to this persistent data structure, and
this is the change you made to this persistent
data structure. The log segment is itself a data
structure in support of these subsystems when implementing the
persistent virtual memory. We have this log segment
as a data structure that the implementation uses to
record changes to persistent portions of the virtual address
space. And because we are putting it into this
data structure called log segment, we can write this
contiguously. And of course, we have to commit these
changes to the disk. But now you can see
the difference in the picture between here and here.
Here we were doing random writes all over the
disk. But here we are writing the changes to
the persistent data structures to contiguous portions of a
log segment and we can take this and store this
contiguously on the disk, and that's the attraction. So
these log segments are going to be stored contiguously
on the disk. And therefore, even though we are
making random writes in the address space of the subsystem
that we are building that requires certain persistent
data structures, because those changes are being recorded as
logs in a log segment, and we are committing the log segment to the disk we can
convert these random writes to sequential writes and
write sequentially on the disk. So two things we
are accomplishing by doing that. The first is
we are not making individual I/O operations for every
time we touch different portions of the virtual address
space, because we are writing it into this log segment,
which may be an in-memory data structure. And therefore,
we are reducing the number of I/O operations that we
need in order to commit these changes finally to
the storage. And second because we are writing this contiguously,
we don't have the random writes that we talked
about. All of this, will help make the design and
implementation of a persistent virtual memory much more
efficient. That is the key idea, which is
there in the paper that I've assigned to
you for reading, which is the LRVM paper.