0% found this document useful (0 votes)
49 views14 pages

Memory Management - II

Segmentation divides a process's virtual address space into logical segments like code, stack, and heap. This allows each segment to be placed independently in physical memory, avoiding wasted space from unused virtual addresses. The hardware uses segment registers containing a base and bounds pair for each segment. During address translation, it identifies the segment using bits of the virtual address, then calculates the physical address by adding the segment's base to the offset within that segment. Supporting stack segments requires tracking whether segments grow in positive or negative directions. Sharing segments between processes is enabled by protection bits indicating read/write permissions on each segment.

Uploaded by

2200031111cseh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
49 views14 pages

Memory Management - II

Segmentation divides a process's virtual address space into logical segments like code, stack, and heap. This allows each segment to be placed independently in physical memory, avoiding wasted space from unused virtual addresses. The hardware uses segment registers containing a base and bounds pair for each segment. During address translation, it identifies the segment using bits of the virtual address, then calculates the physical address by adding the segment's base to the offset within that segment. Supporting stack segments requires tracking whether segments grow in positive or negative directions. Sharing segments between processes is enabled by protection bits indicating read/write permissions on each segment.

Uploaded by

2200031111cseh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

OS - Lecture Memory Management - II

Segmentation Hardware
Segmentation: Generalized Base/Bounds
To solve this problem, an idea was born, and it is called segmentation. It is quite
an old idea, going at least as far back as the very early 1960’s. The idea is
simple: instead of having just one base and bounds pair in our MMU, why not
have a base and bounds pair per logical segment of the address space? A
segment is just a contiguous portion of the address space of a particular length,
and in our canonical address space, we have three logically different
segments: code, stack, and heap. What segmentation allows the OS to do
is to place each one of those segments in different parts of physical
memory, and thus avoid filling physical memory with unused virtual
address space.
Let’s look at an example. Assume we want to place the address space from
Figure 1 into physical memory. With a base and bounds pair per segment, we
can place each segment independently in physical memory. For example, see
Figure 2; there you see a 64KB physical memory with those three segments in it
(and 16KB reserved for the OS). As you can see in the diagram, only used
memory is allocated space in physical memory, and thus large address
spaces with large amounts of unused address space (which we sometimes
call sparse address spaces) can be accommodated. The hardware
Segment Base Size structure in our MMU required to support
segmentation is just what you’d expect: in this
Code 32K 2K case, a set of three base and bounds register
Heap 34K 2K pairs. Figure 16.3 below shows the register values for the example above; each
Stack 28K 2K bounds register holds the size of a segment. Figure 16.3: Segment Register
Values. You can see from the figure that the code segment is placed at physical
address 32KB and has a size of 2KB and the heap segment is placed a t 34KB and also has a size of
2KB.Let’s do an example translation, using the address space in Figure 1. Assume a reference is made to
virtual address 100 (which is in the code segment). When the reference takes place (say, on an instruction
fetch), the hardware will add the base value to the offset into this segment (100 in this case) to arrive at the
desired physical address: 100 + 32KB, or 32868. It will then check that the address is within bounds (100 is
less than 2KB), find that it is, and issue the reference to physical memory address 32868. Now let’s look at an
address in the heap, virtual address 4200 (again refer to Figure 1). If we just add the virtual address 4200 to the base of the heap (34KB), we get a
physical address of 39016, which is not the correct physical address. What we need to first do is extract the offset into the heap, i.e., which byte(s)
in this segment the address refers to. Because the heap starts at virtual address 4KB (4096), the offset of 4200 is actually 4200 – 4096 or 104. We
then take this offset (104) and add it to the base register physical address (34K or 34816) to get the desired result: 34920. What if we tried to refer
to an illegal address, such as 7KB which is beyond the end of the heap? You can imagine what will happen: the hardware detects that the address
is out of bounds, traps into the OS, likely leading to the termination of the offending process. And now you know the origin of the famous term
that all C programmers learn to dread: the segmentation violation or segmentation fault.
Which Segment Are We Referring To?
The hardware uses segment registers during translation. How does it know the offset into a segment, and to which segment an address refers? One
common approach, sometimes referred to as an explicit approach, is to chop up the address space into segments based on the top few bits of the
virtual address; this technique was used in the VAX/VMS system [LL82]. In our example above, we have three segments; thus we need two bits to
accomplish our task. If we use the top two bits of our 14-bit virtual address to select the segment, our virtual address looks like this:

In our example, then, if the top two bits are 00, the hardware knows the virtual address is in the code segment, and thus uses the code base and
bounds pair to relocate the address to the correct physical location. If the top two bits are 01, the hardware knows the address is in the heap, and
thus uses the heap base and bounds. Let’s take our example heap virtual address from above (4200) and translate it, just to make sure this is clear.
The virtual address 4200, in binary form, can be seen here:
As you can see from the picture, the top two bits (01) tell the hardware which segment we are referring to. The bottom 12 bits are the offset into
the segment: 0000 0110 1000, or hex 0x068, or 104 in decimal. Thus, the hardware simply takes the first two bits to determine which segment
register to use, and then takes the next 12 bits as the offset into the segment. By adding the base register to the offset, the hardware arrives at the
final physical address. Note the offset eases the bounds check too: we can simply check if the offset is less than the bounds; if not, the address is
illegal. Thus, if base and bounds were arrays (with one entry per segment), the hardware would be doing something like this to obtain the desired
physical address:
1 // get top 2 bits of 14-bit VA
2 Segment = (VirtualAddress & SEG_MASK) >> SEG_SHIFT
3 // now get offset
4 Offset = VirtualAddress & OFFSET_MASK
5 if (Offset >= Bounds[Segment])
6 RaiseException(PROTECTION_FAULT)
7 else
8 PhysAddr = Base[Segment] + Offset
9 Register = AccessMemory(PhysAddr)
In our running example, we can fill in values for the constants above. Specifically, SEG MASK would be set to 0x3000, SEG SHIFT to 12, and
OFFSET MASK to 0xFFF. You may also have noticed that when we use the top two bits, and we only have three segments (code, heap, stack),
one segment of the address space goes unused. Thus, some systems put code in the same segment as the heap and thus use only one bit to select
which segment to use. There are other ways for the hardware to determine which segment a particular address is in. In the implicit approach, the
1
OS - Lecture Memory Management - II
hardware determines the segment by noticing how the address was formed. If, for example, the address was generated from the program counter
(i.e., it was an instruction fetch), then the address is within the code segment; if the address is based off of the stack or base pointer, it must be in
the stack segment; any other address must be in the heap.
What about the Stack?
Thus far, we’ve left out one important component of the address space: the stack. The stack has been relocated to physical address 28KB in the
diagram above, but with one critical difference: it grows backwards – in physical memory, it starts at 28KB and grows back to 26KB,
corresponding to virtual addresses 16KB to 14KB; translation has to proceed differently. The first thing we need is a little extra hardware support.
Instead of just base and bounds values, the hardware also needs to knowwhich way the segment grows (a bit, for example, that is set to 1 when the
segment grows in the positive direction, and 0 for negative). Our updated view of what the hardware tracks is seen in Table 16.2.
Segment Base Size Grows Positive? Table 2: Segment Registers (With Negative-Growth Support)
Code 32K 2K 1 With the hardware understanding that segments can grow in the negative direction, the hardware
must now
Heap 34K 2K 1 translate such
Stack 28K 2K 0 virtual
addresses
slightly differently. Let’s take an example stack virtual
address and translate it to understand the process. In this
example, assume we wish to access virtual address 15KB,
which should map to physical address 27KB. Our virtual
address, in binary form, thus looks like this: 11 1100 0000
0000 (hex 0x3C00). The hardware uses the top two bits (11)
to designate the segment, but then we are left with an offset
of 3KB. To obtain the correct negative offset, we must
subtract the maximum segment size from 3KB: in this
example, a segment can be 4KB, and thus the correct
negative offset is 3KB - 4KB which equals -1KB. We
simply add the negative offset (-1KB) to the base (28KB) to
arrive at the correct physical address: 27KB. The bounds
check can be calculated by ensuring the absolute value of the
negative offset is less than the segment’s size.
Support for Sharing
As support for segmentation grew, system designers soon realized that they could realize new types of efficiencies with a little more hardware
support. Specifically, to save memory, sometimes it is useful to share certain memory segments between address spaces. In particular, code
sharing is common and still in use in systems today. To support sharing, we need a little extra support from the hardware, in the form of
protection bits. Basic support adds a few bits per segment, indicating whether or not a program can read or write a segment, or perhaps execute
code that lies within the segment. By setting a code segment to read-only, the same code can be shared across multiple processes, without worry of
harming isolation; while each process still thinks that it is accessing its own private memory, the OS is secretly sharing memory which cannot be
modified by the process, and thus the illusion is preserved. An example of the additional information tracked by the hardware (and OS) is shown in
Figure 3. As you can see, the code segment is set to read and execute, and thus the same physical segment in memory could be mapped into
multiple virtual address spaces. With protection bits, the hardware algorithm
described earlier would also have to change. In addition to checking whether a Segment Base Size Grows Positive? Protection
virtual address is within bounds, the hardware also has to check whether a Code 32K 2K 1 Read-Execute
particular access is permissible. If a user process tries to write to a read-only page,
or execute from a non-executable page, the hardware should raise an exception, Heap 34K 2K 1 Read-Write
and thus let the OS deal with the offending process. Table 2: Segment Registers
(With Negative-Growth Support) Stack 28K 2K 0 Read-Write
A segment table maps segment-offset addresses to physical addresses, and
simultaneously checks for invalid addresses, using a system similar to the page tables and relocation base registers discussed previously. ( Note
that at this point in the discussion of segmentation, each segment is
kept in contiguous memory and may be of different sizes, but that
segmentation can also be combined with paging.)
IA-32 Segmentation: The Pentium CPU provides both pure

segmentation and segmentation with paging. In the latter


case, the CPU generates a logical address ( segment-offset
2
OS - Lecture Memory Management - II
pair ), which the segmentation unit converts into a logical linear address, which in turn is mapped to a physical frame by the paging unit, as shown
in Figure: Logical to physical address translation in IA-32.
Memory Management Terms
Frame A fixed-length block of main memory.
A fixed-length block of data that resides in secondary memory (such as a disk). A page of data may temporarily be copied into a
Page
frame of main memory.

A variable-length block of data that resides in secondary memory. An entire segment may temporarily be copied into an available
Segment region of main memory (segmentation) or the segment may be divided into pages, which can be individually copied into main
memory (combined segmentation and paging).
Examples of Logical-to-Physical Address Translation Typical Memory Management Formats

Memory Management Techniques


Technique Description Strengths Weaknesses
Main memory is divided into a number of static Inefficient use of memory due to
Fixed partitions at system generation time. Simple to implement; little internal fragmentation; maximum
Partitioning A process may be loaded into a partition of equal or operating system overhead. number of active processes is
greater size. fixed.
Partitions are created dynamically, so each process Inefficient use of processor due
Dynamic No internal fragmentation; more
is loaded into a partition of exactly the same size as to the need for compaction to
Partitioning efficient use of main memory.
that process. counter external fragmentation.
Main memory is divided into a number of equal-size
frames. Each process is divided into a number of
Pure or A small amount of internal
equal-size pages of the same length as frames. A No external fragmentation.
Simple Paging fragmentation.
process is loaded by loading all of its pages into
available, not necessarily contiguous, frames.
Each process is divided into a number of segments. No internal fragmentation;
Simple A process is loaded by loading all of its segments improved memory utilization and
External fragmentation.
Segmentation into dynamic partitions that need not be reduced overhead compared to
contiguous. dynamic partitioning.
As with simple paging, except that it is not No external fragmentation;
Virtual
necessary to load all of the pages of a process. higher degree of Overhead of complex memory
Memory
Nonresident pages that are needed are multiprogramming; large virtual management.
Paging
automatically brought in later. address space.
No internal fragmentation,
As with simple segmentation, except that it is not
Virtual higher degree of
necessary to load all of the segments of a process. Overhead of complex memory
Memory multiprogramming; large virtual
Nonresident segments that are needed are management.
Segmentation address space; protection and
automatically brought in later.
sharing support.
9.2.2 Performance of Demand Paging
3
OS - Lecture Memory Management - II
Demand paging can significantly affect the performance of a computer system. To see why, let’s compute the effective access time for a demand-
paged memory. For most computer systems, the memory-access time, denoted ma, ranges from 10 to 200 nanoseconds. As long as we have no
page faults, the effective access time is equal to the memory access time. If, however, a page fault occurs, we must first read the relevant page
from disk and then access the desired word.
Let p be the probability of a page fault (0 ≤ p ≤ 1). We would expect p to be close to zero—that is, we would expect to have only a few page faults.
The effective access time is then
effective access time = (1 - p) × ma + p × page fault time.
To compute the effective access time, we must know how much time is needed to service a page fault. A page fault causes the following sequence
to occur:
1. Trap to the operating system.
2. Save the user registers and process state.
3. Determine that the interrupt was a page fault.
4. Check that the page reference was legal and determine the location of the page on the disk.
5. Issue a read from the disk to a free frame:
a. Wait in a queue for this device until the read request is serviced.
b. Wait for the device seek and/or latency time.
c. Begin the transfer of the page to a free frame.
6. While waiting, allocate the CPU to some other user (CPU scheduling, optional).
7. Receive an interrupt from the disk I/O subsystem (I/O completed).
8. Save the registers and process state for the other user (if step 6 is executed).
9. Determine that the interrupt was from the disk.
10. Correct the page table and other tables to show that the desired page is now in memory.
11. Wait for the CPU to be allocated to this process again.
12. Restore the user registers, process state, and new page table, and then resume the interrupted instruction.
Not all of these steps are necessary in every case. For example, we are assuming that, in step 6, the CPU is allocated to another process while the
I/O occurs. This arrangement allows multiprogramming to maintain CPU utilization but requires additional time to resume the page-fault service
routine when the I/O transfer is complete.
In any case, we are faced with three major components of the page-fault service time:
1. Service the page-fault interrupt.
2. Read in the page.
3. Restart the process.
The first and third tasks can be reduced, with careful coding, to several hundred instructions. These tasks may take from 1 to 100 microseconds
each. The page-switch time, however, will probably be close to 8 milliseconds. (A typical hard disk has an average latency of 3 milliseconds, a
seek of 5 milliseconds, and a transfer time of 0.05 milliseconds. Thus, the total paging time is about 8 milliseconds, including hardware and
software time.) Remember also that we are looking at only the device-service time. If a queue of processes is waiting for the device, we have to
add device-queueing time as we wait for the paging device to be free to service our request, increasing even more the time to swap.
With an average page-fault service time of 8 milliseconds and a memory-access time of 200 nanoseconds, the effective access time in nanoseconds
is

We see, then, that the effective access time is directly proportional to the page-fault rate. If one access out of 1,000 causes a page fault, the
effective access time is 8.2 microseconds. The computer will be slowed down by a factor of 40 because of demand paging! If we want
performance degradation to be less than 10 percent, we need
220 > 200 + 7,999,800 × p,
20 > 7,999,800 × p,
p < 0.0000025.
That is, to keep the slowdown due to paging at a reasonable level, we can allow fewer than one memory access out of 399,990 to page-fault. In
sum, it is important to keep the page-fault rate low in a demand-paging system. Otherwise, the effective access time increases, slowing process
execution dramatically.
An additional aspect of demand paging is the handling and overall use of swap space. Disk I/O to swap space is generally faster than that to the file
system. It is faster because swap space is allocated in much larger blocks, and file lookups and indirect allocation methods are not used. The
system can therefore gain better paging throughput by copying an entire file image into the swap space at process startup and then performing
demand paging from the swap space. Another option is to demand pages from the file system initially but to write the pages to swap space as they
are replaced. This approach will ensure that only needed pages are read from the file system but that all subsequent paging is done from swap
space.
Some systems attempt to limit the amount of swap space used through demand paging of binary files. Demand pages for such files are brought
directly from the file system. However, when page replacement is called for, these frames can simply be overwritten (because they are never
modified), and the pages can be read in from the file system again if needed. Using this approach, the file system itself serves as the backing store.
However, swap space must still be used for pages not associated with a file; these pages include the stack and heap for a process. This method
appears to be a good compromise and is used in several systems, including Solaris and BSD UNIX.
Characteristics of Paging and Segmentation:
Simple Paging Virtual Memory Paging Simple Segmentation Virtual Memory Segmentation
Main memory partitioned into small fixed-size
Main memory not partitioned.
chunks called frames.
Program broken into pages by the compiler or Program segments specified by the programmer to the compiler (i.e., the decision is
memory management system. made by the programmer).
Internal fragmentation within frames. No internal fragmentation.
No external fragmentation. External fragmentation.
4
OS - Lecture Memory Management - II

Operating system must maintain a page table for each Operating system must maintain a segment table for each process showing the load
process showing which frame each page occupies. address and length of each segment.

Operating system must maintain a free-frame list. Operating system must maintain a list of free holes in main memory.

Processor uses page number, offset to calculate


Processor uses segment number, offset to calculate absolute address.
absolute address.

All the pages of a Not all pages of a process All the segments of a
process must be in need be in main memory process must be in
Not all segments of a process need be in main memory for the
main memory for frames for the process to main memory for
process to run. Segments may be read in as needed.
process to run, unless run. Pages may be read in as process to run, unless
overlays are used. needed. overlays are used.

Reading a page into main


Reading a segment into main memory may require writing
memory may require writing
one or more segments out to disk.
a page out to disk.
TLB Issue: Context Switches
With TLBs, some new issues arise when switching between processes (and hence address spaces). Specifically, the TLB contains virtual-to-physical translations
that are only valid for the currently running process; these translations are not meaningful for other processes. As a result, when switching from one process to
another, the hardware or OS (or both) must be careful to ensure that the about-to-be-run process does not accidentally use translations from some previously
run process.
To understand this situation better, let’s look at an example. When one process (P1) is running, it assumes the TLB might be caching translations that are valid
for it, i.e., that come from P1’s page table. Assume, for this example, that the 10th virtual page of P1 is mapped to physical frame 100. In this example, assume
another process (P2) exists, and
the OS soon might decide to perform a context switch and run it. Assume here that the 10th virtual page of P2 is mapped
VPN PFN valid prot to physical frame 170. If entries for both processes were in the TLB, the contents of the TLB would be:
In the TLB above, we clearly have a problem: VPN 10 translates to either PFN 100 (P1) or PFN 170 (P2), but the hardware
10 100 1 rwx
can’t distinguish which entry is meant for which process. Thus, we need to do some more work in order for the TLB to
— — 0 — correctly and efficiently support virtualization across multiple processes.
10 170 1 rwx There are a number of possible solutions to this problem. One approach is to simply flush the TLB on context switches,
— — 0 — thus emptying it before running the next process. On a software-based system, this can be accomplished with an explicit
(and privileged) hardware instruction; with a hardware-managed TLB, the flush could be enacted when the page-table
base register is changed (note the OS must change the PTBR on a context switch anyhow). In either case, the flush
operation simply sets all valid bits to 0, essentially clearing the contents of the TLB. VPN PFN valid prot ASID
By flushing the TLB on each context switch, we now have a working solution, as a process will never accidentally
10 100 1 rwx 1
encounter the wrong translations in the TLB. However, there is a cost: each time a process runs, it must incur TLB
misses as it touches its data and code pages. If the OS switches between processes frequently, this cost may be — — 0 — —
high. To reduce this overhead, some systems add hardware support to enable sharing of the TLB across context 10 170 1 rwx 2
switches. In particular, some hardware systems provide an address space identifier (ASID) field in the TLB. You — — 0 — —
can think of the ASID as a process identifier (PID), but usually it has fewer bits (e.g., 8 bits for the ASID versus 32 bits for a PID). If we take our example TLB from
above and add ASIDs, it is clear processes can readily share the TLB: only the ASID field is needed to differentiate otherwise identical translations. Here is a
depiction of a TLB with the added ASID field:
Thus, with address-space identifiers, the TLB can hold translations from different processes at the same time without
any confusion. Of course, the hardware also needs to know which process is currently running in order to perform PFN valid prot present dirty
translations, and thus the OS must, on a context switch, set some privileged register to the ASID of the current
10 1 r-x 1 0
process.
As an aside, you may also have thought of another case where two entries of the TLB are remarkably similar. In this - 0 — - -
example, there are two entries for two different processes with two different VPNs that point to the same physical - 0 — - -
page:
This situation might arise, for example, when two processes share a page (a code page, for example). In the example - 0 — - -
above, Process 1 is sharing physical page 101 with Process 2; P1 maps this page into the 10th page of its address 23 1 rw- 1 1
space, whereas P2 maps it to the 50th page of its address space. Sharing of code pages (in binaries, or shared
libraries) is useful as it reduces the number of physical pages in use, thus reducing memory overheads. - 0 — - -
Hybrid Approach: Paging and Segments - 0 — - -
Whenever you have two reasonable but different approaches to something in life, you should always examine the - 0 — - -
combination of the two to see if you can obtain the best of both worlds. We call such a combination a hybrid. For
- 0 — - -
example, why eat just chocolate or plain peanut butter when you can instead combine the two in a lovely hybrid
known as the Reese’s Peanut Butter Cup [M28]? Years ago, the creators of Multics (in particular Jack Dennis) chanced - 0 — - -
upon such an idea in the construction of the Multics virtual memory system. Specifically, Dennis had the idea of - 0 — - -
combining paging and segmentation in order to reduce the memory overhead of page tables. We can see why this
might work by examining a typical linear page table in more detail. Assume we have an address space in which the - 0 — - -
used portions of the heap and stack are small. For the example, we use a tiny 16KB address space with 1KB pages - 0 — - -
(Figure 20.1); the page table for this address space is in Table 20.1.
- 0 — - -
This example assumes the single code page (VPN 0) is mapped to physical page 10, the single heap page (VPN 4) to
physical page 23, and the two stack pages at the other end of the address space (VPNs 14 and 15) are mapped to 28 1 rw- 1 1
physical pages 28 and 4, respectively. As you can see from the picture, most of the page table is unused, full of invalid 4 1 rw- 1 1
entries. What a waste! And this is for a tiny 16KB address space. Imagine the page table of a 32-bit address space and
all the potential wasted space in there! Actually, don’t imagine such a thing; it’s far too gruesome. Thus, our hybrid approach: instead of having a single page
table for the entire address space of the process, why not have one per logical segment? In this example, we might thus have three page tables, one for the
code, heap, and stack parts of the address space.
Figure 20.1: A 16-KB Address Space with 1-KB Pages
5
OS - Lecture Memory Management - II
Table 20.1: A Page Table For 16-KB Address Space
Now, remember with segmentation, we had a base register that told us where each
segment lived in physical memory, and a bound or limit register that told us the size
of said segment. In our hybrid, we still have those structures in the MMU; here, we
use the base not to point to the segment itself but rather to hold the physical address
of the page table of that segment. The bounds register is used to indicate the end of
the page table (i.e., how many valid pages it has) Let’s do a simple example to clarify.
Assume a 32-bit virtual address space with 4KB pages, and an address space split into
four segments.
We’ll only use three segments for this example: one for code, one for heap, and one
for stack.
To determine which segment an address refers to, we’ll use the top two bits of the
address space. Let’s assume 00 is the unused segment, with 01 for code, 10 for the
heap, and 11 for the stack. Thus, a virtual address looks like this:
In the hardware, assume that there are thus three base/bounds pairs, one each for
code, heap, and stack. When a process is running, the base register for each of these
segments contains the physical address of a linear page table for that segment; thus,
each process in the system now has three page tables associated with it. On a context
switch, these registers must be changed to reflect the location of the page tables of
the newly running process.
On a TLB miss (assuming a hardware-managed TLB, i.e., where the hardware is
responsible for handling TLB misses), the hardware uses the segment bits (SN) to
determine which base and bounds pair to use. The hardware then takes the physical
address therein and combines it with the VPN as follows to form the address of the page table entry (PTE):
SN = (VirtualAddress & SEG_MASK) >> SN_SHIFT
VPN = (VirtualAddress & VPN_MASK) >> VPN_SHIFT
AddressOfPTE = Base[SN] + (VPN * sizeof(PTE))

Address Translation in a Segmentation/Paging System. This sequence should look familiar; it is virtually identical to what we saw before with linear
page tables. The only difference, of course, is the use
of one of three segment base registers instead of the
single page table base register. The critical difference
in our hybrid scheme is the presence of a bounds
register per segment; each bounds register holds the
value of the maximum valid page in the segment. For
example, if the code segment is using its first three
pages (0, 1, and 2), the code segment page table will
only have three entries allocated to it and the bounds
register will be set to 3; memory accesses beyond the
end of the segment will generate an exception and
likely lead to the termination of the process. In this
manner, our hybrid approach realizes a significant
memory savings compared to the linear page table;
unallocated pages between the stack and the heap no
longer take up space in a page table (just to mark
them as not valid). However, as you might notice, this
approach is not without problems. First, it still requires
us to use segmentation; as we discussed before,
segmentation is not quite as flexible as we would like, as it assumes a certain usage pattern of the address space; if we have a large but sparsely-used heap, for
example, we can still end up with a lot of page table waste. Second, this hybrid causes external fragmentation to arise again. While most of memory is managed
in page-sized units, page tables now can be of arbitrary size (in multiples of PTEs). Thus, finding free space for them in memory is more complicated. For these
reasons, people continued to look for better approaches to implementing smaller page tables.
What If Memory Is Full? In the process described above, you may notice that we assumed there is plenty of free memory in which to page in a page
from swap space. Of course, this may not be the case; memory may be full (or close to it). Thus, the OS might like to first page out one or more pages to make
room for the new page(s) the OS is about to bring in. The process of picking a page to kick out, or replace is known as the page-replacement policy. As it turns
out, a lot of thought has been put into creating a good page replacement policy, as kicking out the wrong page can exact a great cost on program performance.
Making the wrong decision can cause a program to run at disk-like speeds instead of memory-like speeds; in current technology that means a program could run
10,000 or 100,000 times slower. Thus, such a policy is something we should study in some detail; indeed, that is exactly what we will do in the next section. For
now, it is good enough to understand that such a policy exists, built on top of the mechanisms described here.
1 VPN = (VirtualAddress & VPN_MASK) >> SHIFT
2 (Success, TlbEntry) = TLB_Lookup(VPN)
3 if (Success == True) // TLB Hit
4 if (CanAccess(TlbEntry.ProtectBits) == True)
5 Offset = VirtualAddress & OFFSET_MASK
6 PhysAddr = (TlbEntry.PFN << SHIFT) | Offset
7 Register = AccessMemory(PhysAddr)
8 else
9 RaiseException(PROTECTION_FAULT)
6
OS - Lecture Memory Management - II
10 else // TLB Miss
11 PTEAddr = PTBR + (VPN * sizeof(PTE))
12 PTE = AccessMemory(PTEAddr)
13 if (PTE.Valid == False)
14 RaiseException(SEGMENTATION_FAULT)
15 else
16 if (CanAccess(PTE.ProtectBits) == False)
17 RaiseException(PROTECTION_FAULT)
18 else if (PTE.Present == True)
19 // assuming hardware-managed TLB
20 TLB_Insert(VPN, PTE.PFN, PTE.ProtectBits)
21 RetryInstruction()
22 else if (PTE.Present == False)
23 RaiseException(PAGE_FAULT)
Figure 21.2: Page-Fault Control Flow Algorithm (Hardware)
Page Fault Control Flow
With all of this knowledge in place, we can now roughly sketch the complete control flow of memory access. In other words, when somebody asks you “what
happens when a program fetches some data from memory?” you should have a pretty good idea of all the different possibilities. See the control flow in Figures
21.2 and 21.3 for more details; the first figure shows what the hardware does during translation, and the second what the OS does upon a page fault. From the
hardware control flow diagram in Figure 21.2, notice that there are now three important cases to understand when a TLB miss occurs. First, that the page was
both present and valid (Lines 18–21); in this case, the TLB miss handler can simply grab the PFN from the PTE, retry the instruction (this tim resulting in a TLB
hit), and thus continue as described (many times) before. In the second case (Lines 22–23), the page fault handler must be run; although this was a legitimate
page for the process to access (it is valid, after all), it is not present in physical memory. Third (and finally), the access could be to an invalid page, due for
example to a bug in the program (Lines 13–14). In this case, no other bits in the PTE really matter; the hardware traps this invalid access, and the OS trap handler
runs, likely terminating the offending process. From the software control flow in Figure 21.3, we can see what the OS rough must do in order to service the page
fault. First, the OS must find a physical frame for the soon-to-be-faulted-in page to reside within; if there is no such page, we’ll have to wait for the replacement
algorithm to run and kick some pages out of memory, thus freeing them for use here.
1 PFN = FindFreePhysicalPage()
2 if (PFN == -1) // no free page found
3 PFN = EvictPage() // run replacement algorithm
4 DiskRead(PTE.DiskAddr, pfn) // sleep (waiting for I/O)
5 PTE.present = True // update page table with present
6 PTE.PFN = PFN // bit and translation (PFN)
7 RetryInstruction() // retry instruction
Figure 21.3: Page-Fault Control Flow Algorithm (Software)
With a physical frame in hand, the handler then issues the I/O request to read in the page from swap space. Finally, when that slow operation completes, the OS
updates the page table and retries the instruction. The retry will result in a TLB miss, and then, upon another retry, a TLB hit, at which point the hardware will be
able to access the desired item.
When Replacements Really Occur
Thus far, the way we’ve described how replacements occur assumes that the OS waits until memory is entirely full, and only then replaces (evicts) a page to
make room for some other page. As you can imagine, this is a little bit unrealistic, and there are many reasons for the OS to keep a small portion of memory free
more proactively. To keep a small amount of memory free, most operating systems thus have some kind of high watermark (HW) and low watermark (LW) to
help decide when to start evicting pages from memory. How this works is as follows: when the OS notices that there are fewer than LW pages available, a
background thread that is responsible for freeing memory runs. The thread evicts pages until there are HW pages available. The background thread, sometimes
called the swap daemon or page daemon1, then goes to sleep, happy that is has freed some memory for running processes and the OS to use. By performing a
number of replacements at once, new performance optimizations become possible. For example, many systems will cluster or group a number of pages and
write them out at once to the swap partition, thus increasing the efficiency of the disk [LL82]; as we will see later when we discuss disks in more detail, such
clustering reduces seek and rotational overheads of a disk and thus increases performance noticeably. To work with the background paging thread, the control
flow in Figure 21.3 should be modified slightly; instead of performing a replacement directly, the algorithm would instead simply check if there are any free
pages available. If not, it would signal that the background paging thread that free pages are needed; when the thread frees up some pages, it would re-awaken
the original thread, which could then page in the desired page and go about its work.
Other VM Policies
Page replacement is not the only policy the VM subsystem employs (though it may be the most important). For example, the OS also has to decide when to bring
a page into memory. This policy, sometimes called the page selection policy (as it was called by Denning), presents the OS with some different options. For most
pages, the OS simply uses demand paging, which means the OS brings the page into memory when it is accessed, “on demand” as it were. Of course, the OS
could guess that a page is about to be used, and thus bring it in ahead of time; this behavior is known as prefetching and should only be done when there is
reasonable chance of success. For example, some systems will assume that if a code page P is brought into memory, that code page P+1will likely soon be
accessed and thus should be brought into memory too. Another policy determines how the OS writes pages out to disk. Of course, they could simply be written
out one at a time; however, many systems instead collect a number of pending writes together in memory and write them to disk in one (more efficient) write.
This behavior is usually called clustering or simply grouping of writes, and is effective because of the nature of disk drives, which perform a single large write
more efficiently than many small ones.
Thrashing
Before closing, we address one final question: what should the OS do When memory is simply oversubscribed, and the memory demands of the set of running
processes simply exceeds the available physical memory? In this case, the system will constantly be paging, a condition sometimes referred to as thrashing.
Some earlier operating systems had a fairly sophisticated set of mechanisms to both detect and cope with thrashing when it took place. For example, given a set
of processes, a system could decide not to run a subset of processes, with the hope that the reduced set of processes working sets (the pages that they are using
actively) fit in memory and thus can make progress. This approach, generally known as admission control, states that it is sometimes better to do less work well
than to try to do everything at once poorly, a situation we often encounter in real life as well as in modern computer systems (sadly). Some current systems take
more a draconian approach to memory overload. For example, some versions of Linux run an out-of-memory killer when memory is oversubscribed; this
daemon chooses a memory intensive process and kills it, thus reducing memory in a none-too-subtle manner. While successful at reducing memory pressure,
this approach can have problems, if, for example, it kills the X server and thus renders any applications requiring the display unusable.
7
OS - Lecture Memory Management - II
Thrashing
• If a process cannot maintain its minimum required number of frames, then it must be swapped out, freeing up frames for other
processes. This is an intermediate level of CPU scheduling.
• But what about a process that can keep its minimum, but cannot keep all of the frames that it is currently using on a regular basis? In
this case it is forced to page out pages that it will need again in the very near future, leading to large numbers of page faults.
• A process that is spending more time paging than executing is said to be thrashing.
9.6.1 Cause of Thrashing
• Early process scheduling schemes would control the level of multiprogramming allowed based on CPU utilization, adding in more
processes when CPU utilization was low.
• The problem is that when memory filled up and processes started spending
lots of time waiting for their pages to page in, then CPU utilization would
lower, causing the schedule to add in even more processes and exacerbating
the problem! Eventually the system would essentially grind to a halt.
• Local page replacement policies can prevent one thrashing process from
taking pages away from other processes, but it still tends to clog up the I/O
queue, thereby slowing down any other process that needs to do even a
little bit of paging ( or any other I/O for that matter. )
• To prevent thrashing we must provide processes with as many frames as
they really need "right now", but how do we know what that is?
• The locality model notes that processes typically access memory references
in a given locality, making lots of references to the same general area of memory before moving periodically to a new locality, as shown
in Figure 9.19 below. If we could just keep as many frames as are involved in the current locality, then page faulting would occur
primarily on switches from one locality to another. ( E.g. when one function exits and another is called. )
9.3 Copy-on-Write
• The idea behind a copy-on-write fork is that the pages for a parent process do not have to be actually copied for the child until one or
the other of the processes changes the page. They can be simply shared between the two processes in the meantime, with a bit set that
the page needs to be copied if it ever gets written to. This is a reasonable approach, since the child process usually issues an exec( )
system call immediately after the fork.

Before process 1 modifies page C.


After process 1 modifies page C.
Obviously only pages that can be modified even need to be labeled as copy-on-write. Code segments can simply be shared.
• Pages used to satisfy copy-on-write duplications are typically allocated using zero-fill-on-demand, meaning that their previous contents
are zeroed out before the copy proceeds.
• Some systems provide an alternative to the fork( ) system call called a virtual memory fork, vfork( ). In this case the parent is
suspended, and the child uses the parent's memory pages. This is very fast for process creation, but requires that the child not modify
any of the shared memory pages before performing the exec( ) system call. ( In essence this addresses the question of which process
executes first after a call to fork, the parent or the child. With vfork, the parent is suspended, allowing the child to execute first until it
calls exec( ), sharing pages with the parent in the
meantime.
Page Replacement Algorithms
When a page fault occurs, the operating system has to choose a
page to remove from memory to make room for the page that has to
be brought in. If the page to be removed has been modified while in
memory, it must be rewritten to the disk to bring the disk copy up
to date. If, however, the page has not been changed (e.g., it contains
program text), the disk copy is already up to date, so no rewrite is
needed. The page to be read in just overwrites the page being
evicted. While it would be possible to pick a random page to evict
at each page fault, system performance is much better if a page that
is not heavily used is chosen. If a heavily used page is removed, it
will probably have to be brought back in quickly, resulting in extra
overhead. Much work has been done on the subject of page
replacement algorithms, both theoretical and experimental. Below
we will describe some of the most important algorithms.
Figure: Steps in Handling a Page Fault
It is worth noting that the problem of "page replacement" occurs in
other areas of computer design as well. For example, most
computers have one or more memory caches consisting of recently
used 32-byte or 64-byte memory blocks. When the cache is full,
8
OS - Lecture Memory Management - II
some block has to be chosen for removal. This problem is precisely the same as page replacement except on a shorter time scale (it has to be done
in a few nanoseconds, not milliseconds as with page replacement). The reason for the shorter time scale is that cache block misses are satisfied
from main memory, which has no seek time and no rotational latency.
A second example is in a web browser. The browser keeps copies of previously accessed web pages in its cache on the disk. Usually, the
maximum cache size is fixed in advance, so the cache is likely to be full if the browser is used a lot. Whenever a web page is referenced, a check is
made to see if a copy is in the cache and if so, if the page on the web is newer. If the cached copy is up to date, it is used; otherwise, a fresh copy is
fetched from the Web. If the page is not in the cache at all or a newer version is available, it is downloaded. If it is a newer copy of a cached page
it replaces the one in the cache. When the cache is full a decision has to be made to evict some other page in the case of a new page or a page that
is larger than an older version. The considerations are similar to pages of virtual memory, except for the fact that the Web pages are never
modified in the cache and thus are never written back to the web server. In a virtual memory system, pages in main memory may be either clean or
dirty.
4.4.1. The Optimal Page Replacement Algorithm
The best possible page replacement algorithm is easy to describe but impossible to implement. It goes like this. At the moment that a page fault
occurs, some set of pages is in memory. One of these pages will be referenced on the very next instruction (the page containing that instruction).
Other pages may not be referenced until 10, 100, or perhaps 1000 instructions later. Each page can be labeled with the number of instructions that
will be executed before that page is first referenced.
The optimal page algorithm simply says that the page with the highest label should be removed. If one page will not be used for 8 million
instructions and another page will not be used for 6 million instructions, removing the former pushes the page fault that will fetch it back as far
into the future as possible. Computers, like people, try to put off unpleasant events for as long as they can.
The only problem with this algorithm is that it is unrealizable. At the time of the page fault, the operating system has no way of knowing when
each of the pages will be referenced next. (We saw a similar situation earlier with the shortest-job-first scheduling algorithm how can the system
tell which job is shortest?) Still, by running a program on a simulator and keeping track of all page references, it is possible to implement optimal
page replacement on the second run by using the page reference information collected during the first run.
In this way it is possible to compare the performance of realizable algorithms with the best possible one. If an operating system achieves a
performance of, say, only 1 percent worse than the optimal algorithm, effort spent in looking for a better algorithm will yield at most a 1 percent
improvement.
To avoid any possible confusion, it should be made clear that this log of page references refers only to the one program just measured and then
with only one specific input. The page replacement algorithm derived from it is thus specific to that one program and input data. Although this
method is useful for evaluating page replacement algorithms, it is of no use in practical systems. Below we will study algorithms that are useful on
real systems.
4.4.2. The Not Recently Used Page Replacement Algorithm
In order to allow the operating system to collect useful statistics about which pages are being used and which ones are not, most computers with
virtual memory have two status bits associated with each page. R is set whenever the page is referenced (read or written). M is set when the page is
written to (i.e., modified). The bits are contained in each page table entry, as shown in Fig. 4-11. It is important to realize that these bits must be
updated on every memory reference, so it is essential that they be set by the hardware. Once a bit has been set to 1, it stays 1 until the operating
system resets it to 0 in software.
If the hardware does not have these bits, they can be simulated as follows. When a process is started up, all of its page table entries are marked as
not in memory. As soon as any page is referenced, a page fault will occur. The operating system then sets the R bit (in its internal tables), changes
the page table entry to point to the correct page, with mode READ ONLY, and restarts the instruction. If the page is subsequently written on,
another page fault will occur, allowing the operating system to set the M bit as well and change the page's mode to READ/WRITE.
The R and M bits can be used to build a simple paging algorithm as follows. When a process is started up, both page bits for all its pages are set to
0 by the operating system. Periodically (e.g., on each clock interrupt), the R bit is cleared, to distinguish pages that have not been referenced
recently from those that have been.
When a page fault occurs, the operating system inspects all the pages and divides them into four categories based on the current values of their R
and M bits:
Class 0: not referenced, not modified.
Class 1: not referenced, modified.
Class 2: referenced, not modified.
Class 3: referenced, modified.
Although class 1 pages seem, at first glance, impossible, they occur when a class 3 page has its R bit cleared by a clock interrupt. Clock interrupts
do not clear the M bit because this information is needed to know whether the page has to be rewritten to disk or not. Clearing R but not M leads to
a class 1 page. The NRU (Not Recently Used) algorithm removes a page at random from the lowest numbered nonempty class. Implicit in this
algorithm is that it is better to remove a modified page that has not been referenced in at least one clock tick (typically 20 msec) than a clean page
that is in heavy use. The main attraction of NRU is that it is easy to understand, moderately efficient to implement, and gives a performance that,
while certainly not optimal, may be adequate.
4.4.3. The First-In, First-Out (FIFO) Page Replacement Algorithm
Another low-overhead paging algorithm is the FIFO (First-In, First-Out) algorithm. To illustrate how this works, consider a supermarket that has
enough shelves to display exactly k different products. One day, some company introduces a new convenience foodinstant, freeze-dried, organic
yogurt that can be reconstituted in a microwave oven. It is an immediate success, so our finite supermarket has to get rid of one old product in
order to stock it.
One possibility is to find the product that the supermarket has been stocking the longest (i.e., something it began selling 120 years ago) and get rid
of it on the grounds that no one is interested any more. In effect, the supermarket maintains a linked list of all the products it currently sells in the
order they were introduced. The new one goes on the back of the list; the one at the front of the list is dropped.
As a page replacement algorithm, the same idea is applicable. The operating system maintains a list of all pages currently in memory, with the
page at the head of the list the oldest one and the page at the tail the most recent arrival. On a page fault, the page at the head is removed and the
new page added to the tail of the list. When applied to stores, FIFO might remove mustache wax, but it might also remove flour, salt, or butter.
When applied to computers the same problem arises. For this reason, FIFO in its pure form is rarely used.
4.4.4. The Second Chance Page Replacement Algorithm
A simple modification to FIFO that avoids the problem of throwing out a heavily used page is to inspect the R bit of the oldest page. If it is 0, the
page is both old and unused, so it is replaced immediately. If the R bit is 1, the bit is cleared, the page is put onto the end of the list of pages, and
its load time is updated as though it had just arrived in memory. Then the search continues.
9
OS - Lecture Memory Management - II
The operation of this algorithm, called second chance, is shown in Fig. 4-14. In Fig. 4-14(a) we see pages A through H kept on a linked list and
sorted by the time they arrived in memory.
Figure 4-14. Operation of second chance. (a) Pages sorted in FIFO order. (b) Page list if a page fault occurs at time 20 and A has its R bit
set. The numbers above the pages are their loading times.
Suppose that a page fault occurs at time 20. The
oldest page is A, which arrived at time 0, when the
process started. If A has the R bit cleared, it is
evicted from memory, either by being written to
the disk (if it is dirty), or just abandoned (if it is
clean). On the other hand, if the R bit is set, A is
put onto the end of the list and its "load time" is
reset to the current time (20). The R bit is also
cleared. The search for a suitable page continues
with B.
What second chance is doing is looking for an old
page that has not been referenced in the previous
clock interval. If all the pages have been referenced, second chance degenerates into pure FIFO. Specifically, imagine that all the pages in Fig. 4-
14(a) have their R bits set. One by one, the operating system moves the pages to the end of the list, clearing the R bit each time it appends a page
to the end of the list. Eventually, it comes back to page A, which now has its R bit cleared. At this point A is evicted. Thus the algorithm always
terminates.
4.4.5. The Clock Page Replacement Algorithm
Although second chance is a reasonable algorithm, it is unnecessarily inefficient because it is constantly moving pages around on its list. A better
approach is to keep all the page frames on a circular list in the form of a clock, as shown in Fig. 4-15. A hand points to the oldest page.
Figure 4-15. The clock page replacement algorithm.
When a page fault occurs, the page being pointed to by the hand is
inspected. If its R bit is 0, the page is evicted, the new page is inserted into
the clock in its place, and the hand is advanced one position. If R is 1, it is
cleared and the hand is advanced to the next page. This process is repeated
until a page is found with R = 0. Not surprisingly, this algorithm is called
clock. It differs from second chance only in the implementation, not in the
page selected.
4.4.6. The Least Recently Used (LRU) Page Replacement Algorithm
A good approximation to the optimal algorithm is based on the
observation that pages that have been heavily used in the last few
instructions will probably be heavily used again in the next few.
Conversely, pages that have not been used for ages will probably remain
unused for a long time. This idea suggests a realizable algorithm: when a
page fault occurs, throw out the page that has been unused for the longest time. This strategy is called LRU (Least Recently Used) paging.
Although LRU is theoretically realizable, it is not cheap. To fully implement LRU, it is necessary to maintain a linked list of all pages in memory,
with the most recently used page at the front and the least recently used page at the rear. The difficulty is that the list must be updated on every
memory reference. Finding a page in the list, deleting it, and then moving it to the front is a very time-consuming operation, even in hardware
(assuming that such hardware could be built).
However, there are other ways to implement LRU with special hardware. Let us consider the simplest way first. This method requires equipping
the hardware with a 64-bit counter, C, that is automatically incremented after each instruction. Furthermore, each page table entry must also have a
field large enough to contain the counter. After each memory reference, the current value of C is stored in the page table entry for the page just
referenced. When a page fault occurs, the operating system examines all the counters in the page table to find the lowest one. That page is the least
recently used.
Now let us look at a second hardware LRU algorithm. For a machine with n page frames, the LRU hardware can maintain a matrix of n x n bits,
initially all zero. Whenever page frame k is referenced, the hardware first sets all the bits of row k to 1, then sets all the bits of column k to 0. At
any instant, the row whose binary value is lowest is the least recently used, the row whose value is next lowest is next least recently used, and so
forth. The workings of this algorithm are given in Fig. 4-16 for four page frames and page references in the order
Figure 4-16. LRU using a matrix when pages
are referenced in the order 0, 1, 2, 3, 2, 1, 0, 3,
2, 3.
0123210323
After page 0 is referenced, we have the situation
of Fig. 4-16(a). After page 1 is referenced, we
have the situation of Fig. 4-16(b), and so forth.
4.5. Design Issues for Paging Systems
In the previous sections we have explained how
paging works and have given a few of the basic
page replacement algorithms and shown how to
model them. But knowing the bare mechanics is
not enough. To design a system, you have to know
a lot more to make it work well. It is like the
difference between knowing how to move the
rook, knight, and other pieces in chess, and being
a good player. In the following sections, we will
look at other issues that operating system
designers must consider in order to get good
performance from a paging system.
9.6.2 Working-Set Model
10
OS - Lecture Memory Management - II
• The working set model is based on the concept of locality, and defines a working set window, of length delta. Whatever pages are
included in the most recent delta page references are said to be in the processes working set window, and comprise its current working
set, as illustrated in Figure 9.20:
Figure 9.20 - Working-set model.
• The selection of delta is critical to the success
of the working set model - If it is too small
then it does not encompass all of the pages of
the current locality, and if it is too large, then it
encompasses pages that are no longer being
frequently accessed.
• The total demand, D, is the sum of the sizes of the working sets for all processes. If D exceeds the total number of available frames, then
at least one process is thrashing, because there are not enough frames available to satisfy its minimum working set. If D is significantly
less than the currently available frames, then additional processes can be launched.
• The hard part of the working-set model is keeping track of what pages are in the current working set, since every reference adds one to
the set and removes one older page. An approximation can be made using reference bits and a timer that goes off after a set interval of
memory references:
o For example, suppose that we set the timer to go off after every 5000 references ( by any process ), and we can store two
additional historical reference bits in addition to the current reference bit.
o Every time the timer goes off, the current reference bit is copied to one of the two historical bits, and then cleared.
o If any of the three bits is set, then that page was referenced within the last 15,000 references, and is considered to be in that
processes reference set.
o Finer resolution can be achieved with more historical bits and a more frequent timer, at the expense of greater overhead.
9.6.3 Page-Fault Frequency
• A more direct approach is to recognize that what we really want to control is the page-fault rate, and to allocate frames based on this
directly measurable value. If the page-fault rate exceeds a certain upper bound then that process needs more frames, and if it is below a
given lower bound, then it can afford to give up some of its frames to other processes.
• ( I suppose a page-replacement strategy could be devised that would select victim frames based on the process with the lowest current
page-fault frequency. )

Figure 9.21 - Page-fault frequency.


• Note that there is a direct relationship between the page-fault rate and the working-set, as a process moves from one locality to another:
4.5.2. Local versus Global Allocation Policies
In the preceding sections we have discussed several algorithms for choosing a page to replace when a fault occurs. A major issue associated with
this choice (which we have carefully swept under the rug until now) is how memory should be allocated among the competing runnable processes.
Take a look at Fig. 4-19(a). In this figure, three processes, A, B, and C, make up the set of runnable processes. Suppose A gets a page fault. Should
the page replacement algorithm try to find the least recently used page considering only the six pages currently allocated to A, or should it consider
all the pages in memory? If it looks only at A's pages, the page with the lowest age value is A5, so we get the situation of Fig. 4-19(b).
Figure 4-19. Local versus global page replacement. (a) Original configuration. (b) Local page replacement. (c) Global page replacement.
On the other hand, if the page with the lowest age value is removed without regard to whose page it is, page B3 will be chosen and we will get the
situation of Fig. 4-19(c). The algorithm of Fig. 4-
19(b) is said to be a local page replacement
algorithm, whereas that of Fig. 4-19(c) is said to be
a global algorithm. Local algorithms effectively
correspond to allocating every process a fixed
fraction of the memory. Global algorithms
dynamically allocate page frames among the
runnable processes. Thus the number of page
frames assigned to each process varies in time.
In general, global algorithms work better,
especially when the working set size can vary over
the lifetime of a process. If a local algorithm is
used and the working set grows, thrashing will
result, even if there are plenty of free page frames.
If the working set shrinks, local algorithms waste
memory. If a global algorithm is used, the system
must continually decide how many page frames to
assign to each process. One way is to monitor the
working set size as indicated by the aging bits, but
this approach does not necessarily prevent
thrashing. The working set may change size in microseconds, whereas the aging bits are a crude measure spread over a number of clock ticks.
11
OS - Lecture Memory Management - II
Another approach is to have an algorithm for allocating page frames to processes. One way is to periodically determine the number of running
processes and allocate each process an equal share. Thus with 12,416 available (i.e., nonoperating system) page frames and 10 processes, each
process gets 1241 frames. The remaining 6 go into a pool to be used when page faults occur.
Although this method seems fair, it makes little sense to give equal shares of the memory to a 10-KB process and a 300-KB process. Instead, pages
can be allocated in proportion to each process' total size, with a 300-KB process getting 30 times the allotment of a 10-KB process. It is probably
wise to give each process some minimum number, so it can run, no matter how small it is. On some machines, for example, a single two-operand
instruction may need as many as six pages because the instruction itself, the source
operand, and the destination operand may all straddle page boundaries. With an
allocation of only five pages, programs containing such instructions cannot execute
at all.. Figure 4-20. Page fault rate as a function of the number of page frames
assigned.
If a global algorithm is used, it may be possible to start each process up with some
number of pages proportional to the process' size, but the allocation has to be
updated dynamically as the processes run. One way to manage the allocation is to
use the PFF (Page Fault Frequency) algorithm. It tells when to increase or decrease a
process' page allocation but says nothing about which page to replace on a fault. It
just controls the size of the allocation set. For a large class of page replacement
algorithms, including LRU, it is known that the fault rate decreases as more pages are assigned, as we discussed above. This is the assumption
behind PFF. This property is illustrated in Fig. 4-20.
Fetch Policy
The fetch policy determines when a page should be brought into main memory. The two common alternatives are demand paging and prepaging.
Demand paging: The transfer of a page from secondary memory to main memory storage at the moment of need.
Prepaging: The retrieval of pages other than the one demanded by a page fault. The hope is that the additional pages will be needed in the near
future, conserving disk I/O.
Belady’s Anomaly
Intuitively, it might seem that the more page frames the memory has, the fewer page faults a program will get. Surprisingly enough, this is not
always the case. Belady et al. (1969) discovered a counterexample, in which FIFO caused more page faults with four page frames than with three.
This strange situation has become known as Belady’s anomaly. It is illustrated in Fig. 4-24 for a program with five virtual pages, numbered from
0 to 4. The pages are referenced in the order
012301401234
In Fig. 4-24(a) we see how with three page frames a total of nine page faults are caused. In Fig. 4-24(b) we get ten page faults with four page
frames.

Figure 4-24. Belady’s anomaly. (a) FIFO with three page frames. (b) FIFO with four page frames. The P’s show which page references cause
page faults.
Behaviour of Four Page Replacement Algorithms. The example assumes a fixed frame allocation (fixed resident set size) for this process of
three frames. The execution of the process requires reference to five distinct pages. The page address stream formed by executing the program is
which means that the first page referenced is 2, the second page referenced is 3, and so on. The optimal policy produces three page faults after the
frame allocation has been filled.
The placement policy: determines
where in real memory a process piece
is to reside. In a pure segmentation
system, policies such as best-fit, first-
fit, and so on, are possible
alternatives. However, for a system
that uses either pure paging or paging
combined with segmentation,
placement is usually irrelevant.
Resident Set Management:
1. How many page frames are to be
allocated to each active process.
2. Whether the set of pages to be
considered for replacement should be
limited to those of the process that
caused the page fault or encompass all
the page frames in main memory
Replacement policy:
Among the set of pages considered,
which particular page should be
selected for replacement.
Frame Locking:
Some of the frames in main memory
may be locked. When a frame is
locked, the page currently stored in
that frame may not be replaced.
12
OS - Lecture Memory Management - II

Page Replacement Algorithms


This handout shows how the various page replacement algorithms work. We shall call the pages of the program a,
b, c, ... to distinguish them from the time (1, 2, 3, ...).
Fixed Number of Frames
We shall demonstrate these algorithms by running them on the reference string ω = cadbebabcd and assume that,
initialy, pages a, b, c, and doccupy frames 0, 1, 2, and 3 respectively. When appropriate, the little arrow → indicates
the location of the “pointer” that indicates where the search for the next victim will begin.
First In/First Out (FIFO) This policy replaces pages in the order of arrival in memory.
time 0 1 2 3 4 5 6 7 8 9 10
ω c a d b e b a b c d
frame 0 →a →a →a →a →a e e e e →e d
frame 1 b b b b b →b →b a a a →a
frame 2 c c c c c c c →c b b b
frame 3 d d d d d d d d →d c c
page fault 1 2 3 4 5
page(s) loaded e a b c d
page(s) removed a b c d e
Optimal (OPT, MIN) This policy selects for replacement the page that will not be referenced for the longest time in the future.
time 0 1 2 3 4 5 6 7 8 9 10
ω c a d b e b a b c d The optimal policy selects for
frame 0 a a a a a a a a a a d replacement that page for
frame 1 b b b b b b b b b b b which the time to the next
frame 2 c c c c c c c c c c c reference is the longest
frame 3 d d d d d e e e e e e
page fault 1 2
page(s) loaded e d
page(s) removed d a
Least Recently Used (LRU) This policy selects for replacement the page that has not been used for the longest period of time.
time 0 1 2 3 4 5 6 7 8 9 10
ω c a d b e b a b c d
frame 0 a a a a a a a a a a a
frame 1 b b b b b b b b b b b
frame 2 c c c c c e e e e e d
frame 3 d d d d d d d d d c c
page fault 1 2 3
page(s) loaded e c d
page(s) removed c d e
stack (top) c a d b e b a b c d
– c a d b e b a b c
– – c a d d e e a b
stack (bottom) – – – c a a d d e a
Not-Recently-Used or Not Used Recently (NRU, NUR)
This policy selects for replacement a random page from the following classes (in the order given): not used or modified,
not used but modified, used and not modified, used and modified. In the following, assume references at times 2, 4,
and 7 are writes (represented by the bold page references). The two numbers written after each page are the use and
modified bits, respectively.

time 0 1 2 3 4 5 6 7 8 9 10
ω c a d b e b a b c d
frame 0 a/00 a/00 a/11 a/11 a/11 a/01 a/01 a/11 a/11 a/01 a/01
frame 1 b/00 b/00 b/00 b/00 b/11 b/01 b/11 b/11 b/11 b/01 b/01
frame 2 c/00 c/10 c/10 c/10 c/10 e/10 e/10 e/10 e/10 e/00 d/10
frame 3 d/00 d/00 d/00 d/10 d/10 d/00 d/00 d/00 d/00 c/10 c/00
page fault 1 2 3
page(s) loaded e c d
page(s) removed c d e
13
OS - Lecture Memory Management - II
Clock This policy is similar to LRU and FIFO. Whenever a page is referenced, the use bit is set. When a page must be
replaced, the algorithm begins with the page frame pointed to. If the frame’s use bit is set, it is cleared and the pointer
advanced. If not, the page in that frame is replaced. Here the number after the page is the use bit; we’ll assume all
pages have been referenced initially.
time 0 1 2 3 4 5 6 7 8 9 10
ω c a d b e b a b c d
frame 0 a/0 →a/0 →a/1 →a/1 →a/1 e/1 e/1 e/1 e/1 →e/1 d/1
frame 1 b/0 b/0 b/0 b/0 b/1 →b/0 →b/1 b/0 b/1 b/1 b/0
frame 2 c/0 c/1 c/1 c/1 c/1 c/0 c/0 a/1 a/1 a/1 a/0
frame 3 d/0 d/0 d/0 d/1 d/1 d/0 d/0 →d/0 →d/0 c/1 c/0
page fault 1 2 3 4
page(s) loaded e a c d
page(s) removed a c d e
Second-chance Cyclic
This policy merges the clock algorithm and the NRU algorithm. Each page frame has a use and a modified bit. Whenever a
page is referenced, the use bit is set; whenever modified, the modify bit is set. When a page must be replaced, the algorithm
begins with the page frame pointed to. If the frame’s use bit and modify bit are set, the use bit is cleared and the pointer advanced;
if the use bit is set but the modify bit is not, the use bit is cleared and the pointer advanced; if the use bit is clear but the modify
bit is set, the modify bit is cleared (and the algorithm notes that the page must be copied out before being replaced; here, the
page is emboldened) and the pointer is advanced; if both the use and modify bits are clear, the page in that frame is replaced. In
the following, assume references at times 2, 4, and 7 are writes (represented by the bold page references). The two numbers
written after each page are the use and modified bits, respectively. Initially, all pages have been used but none are modified.
time 0 1 2 3 4 5 6 7 8 9 10
ω c a d b e b a b c d
frame 0 a/00 →a/00 →a/11 →a/11 →a/11 a/00 a/00 a/11 a/11 →a/11 a/00
frame 1 b/00 b/00 b/00 b/00 b/11 b/00 b/10 b/10 b/10 b/10 d/10
frame 2 c/00 c/10 c/10 c/10 c/10 e/10 e/10 e/10 e/10 e/10 →e/00
frame 3 d/00 d/00 d/00 d/10 d/10 →d/00 →d/00 →d/00 →d/00 c/10 c/00
fault 1 2 3
loaded e c d
removed c d e
Variable Number of Frames
Working Set (WS)
This policy tries to keep all pages in a process’ working set in memory. This table shows the pages consitiuting the working set
at each reference. Here, we take the working set to be that set of pages which has been referenced during the last τ = 4 units.
We also assume that a was referenced at time 0, d at time −1, and e at time −2. The window begins with the current reference.
time −2 −1 0 1 2 3 4 5 6 7 8 9 10
ω e d a c a d b e b a b c d
page a – – a a a a a a – a a a a
page b – – – – – – b b b b b b b
page c – – – c c c c – – – – c c
page d – d d d d d d d d – – – d
page e e e e e – – – e e e e – –
page fault 1 2 3 4 5 6
page(s) loaded c b e a c d
page(s) removed e c a d e
Page Fault Frequency (PFF)
This approximation to the working set policy tries to keep page faulting to some prespecified range. If the time between the current
and the previous page fault exceeds some critical value p, then all pages not referenced between those page faults are removed.
This table shows the pages resident at each reference. Here, we take p = 2 units and assume that initially, a, d, and e are resident.
This example assumes the interval between page faults does not include the reference that caused the previous page fault.
time 0 1 2 3 4 5 6 7 8 9 10
ω c a d b e b a b c d
page a a a a a a a a a a a a
page b – – – – b b b b b – –
page c – c c c – – – – – c c
page d d d d d d d d d d – d
page e e e e e – e e e e – –
page fault 1 2 3 4 5
page(s) loaded c b e c d
page(s) removed c,e d,e
14

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy