YouTube Transcript:
Why Can't Programs Access Each Other's Memory?
Skip watching entire videos - get the full transcript, search for keywords, and copy with one click.
Share:
Video Transcript
This video was sponsored by Brilliant.
If I were to ask you what the most
important part of a computer is right
after the CPU, the answer would most
likely be memory.
We've previously talked about circuits
that can hold a binary value, either
using transistors or capacitors to
create a memory cell that can store a
one or a zero. We learned that
physically speaking, memory is
essentially a giant grid of these tiny
cells where each cell can be accessed
using a binary value called an address.
This term address originates from
hardware design. Since each memory cell
has a unique position in this matrix, a
binary address combined with additional
circuitry can be used to pinpoint the
exact cell we want to read from or write to.
to.
With a single bit of information, we can
represent basic values like true or
false, day or night, black or white. But
that's it. Only data with two possible
states. To represent more complex data,
we need more bits. That's why computers
don't associate an address with just one
bit, but with several. In computer
science, this unit is traditionally
called a word. But the more common term
you'll hear is a bite.
A bite typically consists of eight bits.
And that's a lot more useful because 8
bits allows to represent values with up
to 256 different states accessible with
a single memory address. The good news
is that as I explained in my videos
about building memory from transistors,
we the developers don't have to think of
memory as a grid of circuits. From our
perspective, memory behaves like a
massive array of bytes, each with its
own unique address.
Memory is central to the operation of a
modern computer system because main
memory and the registers built into each
CPU core are the only general purpose
storage that the CPU can access directly.
directly.
There are machine instructions like load
and store that take memory addresses as
arguments to make the CPU read from
specific memory locations or write to a
specific memory location.
But there are no instructions that take
disk addresses. Therefore, any
instruction being executed and any data
it operates on must reside in one of
these direct access storage devices. If
the data isn't already in memory, it
must be moved there before the CPU can
use it.
For example, even a trivial operation
like incrementing a counter involves
multiple memory operations.
Each instruction requires a memory read
because the CPU must fetch it from
memory before it can execute it. The
load instruction tells the CPU to read a
value from a specific memory address.
That's another memory read operation.
And the store instruction tells the CPU
to write a value to a specific memory
location, which is a memory write operation.
Okay, but up to this point, I haven't
said anything you haven't already heard
in previous videos. So, what's the point
Well, once again, concurrency makes
Hi friends, my name is George and this
is Core Dumpt.
Before starting, consider giving this
video a like. It helps others discover
my content, which by the way is free.
As we've discussed before, running
multiple programs simultaneously on the
same system introduces several
challenges. When programs run
concurrently, they must share the CPU.
So, scheduling algorithms are used to
ensure fair access to computing time.
But concurrency doesn't just affect CPU
time, it also affects memory.
Programs typically reside on disk. But
as we mentioned earlier, there are no
machine instructions that use disk addresses.
addresses.
So when we launch a program, it must be
loaded into memory so the CPU can fetch
and execute its instructions and data
becoming a process.
Each process also needs memory for
variables, temporary values, and other
kind of data. The total memory allocated
to a process is known as its address space.
space.
Here's the rule we want to establish. By
default, a process should never be able
to access the address space of another
process. But why?
Well, let me give you an example.
Imagine two processes running
concurrently. The first one displays a
payment form that allows a user to send
money to someone else. As the user fills
out the form, their input is stored in a
buffer somewhere in that process's
address space.
If memory access restrictions are not
enforced, the second process or any
other process could simply execute a
small piece of code using load and store
instructions to read that payment data
from the first process's address space,
copy it into its own address space, and
send it over the network to a malicious
actor. That would be a serious problem,
especially considering how casually we
install apps without knowing who
developed them. And since the operating
system itself also relies on memory to
manage the system, unprotected memory
access is something no modern OS can
afford to overlook.
And it's not just reading that's
dangerous. If a process could access
another process's address space, it
could also write to it. A malicious
process could alter a buffer to change
the payment amount or the recipient
without the user ever knowing. Worse
yet, without memory protection, one
process could even modify another
process's code or even the code of the
operating system, injecting malicious
executable code to make it do virtually anything.
The address space of a process is
sacred. A process should never be able
to read from or write to the memory of another.
another.
As with most things in computer science,
there are multiple ways to enforce this
rule, but doing it entirely in software
is not one of them.
As mentioned earlier, memory operations
happen constantly while a process is
running. If the operating system had to
manually check for illegal memory access
for each process that is currently using
the CPU, it would need to run extra code
on every single memory operation the
user process performs.
That's not a trivial task. Since
instructions need to be fetched from
memory, for every instruction the user
process executes, the operating system
might have to run several more just to
validate the memory access, introducing
a massive performance overhead. So
hardware support is needed. Now, I know
circuitry usually scares developers,
especially if you're trying to make
sense of it just from textbooks. That's
why we focus on breaking things down
visually here and why Brilliant is such
a great companion for this kind of
learning. If you're someone who loves
learning new things and building real
skills, but feels like endlessly
scrolling through PDFs isn't the way to
do it, then Brilliant is for you.
Brilliant is a learning platform built
around active problem solving. Every
lesson is designed to let you play with
ideas using a first principles approach
that helps you understand concepts from
the ground up. You'll stay motivated
with a perfect blend of engaging
challenges, daily streaks, and
competitive features that actually make
learning feel rewarding. All content on
Brilliant is crafted by an award-winning
team of teachers, researchers, and
professionals from Stanford, MIT,
Caltech, Microsoft, Google, and more.
The best part is Brilliant is available
right on your phone. So instead of
passively scrolling PDFs, you can learn
a little every day, wherever you are.
Courses like thinking in code and
programming with variables help you
develop the mindset of a programmer by
teaching you to break down complex
problems into logical manageable parts
and also help you develop an intuition
for computer logic as you learn to
design and debug real programs.
To try everything Brilliant has to offer
for free for a full 30 days and get 20%
off in your annual subscription, visit brilliant.org/coredumpt
brilliant.org/coredumpt
or scan the QR code on screen or click
on the link in the description below.
And now back to the video.
You might think we need to modify the
memory itself. But actually memory
remains untouched in this design. While
a process runs, the memory unit sees
only a stream of memory addresses coming
from the CPU through this set of
parallel wires called the address bus.
The main memory does not know how those
addresses are generated or what they are
for. So handling this process directly
in memory is not the best way. Now, I
don't want to draw a million wires, so
I'll use the simpler architecture I
usually show here in the channel.
There's little to no use for general
purpose components. In this context,
we'll use two special registers to
enforce memory boundaries. The base
register points to the smallest legal
physical memory address of the process.
The limit register defines the size of
the addressable range for that process.
With these registers, we can check
whether any address a process tries to
access falls within its own address space.
space.
In code terms, we're checking whether
the address is greater than or equal to
the base and lower than the sum of the
base plus the limit. It's the same logic
the OS would follow if the check were
done in software, but here we're
translating it into hardware.
Note that the sum of the base and limit
gives the upper bound of the address
space. But this bound is exclusive,
which is why we check that the address
is less than not equal to the upper bound.
bound.
The good news is that calculating this
upper bound is as simple as adding the
contents of the base and limit registers
using a binary adder which we covered in
a previous episode.
Remember we want to validate the address
being passed on every single memory
operation. And since every address
passes through the address bus, we can
intercept the address right there. Now
that we have all the values we need, we
can move on to the comparisons.
To compare the address with the
boundaries, we'll use binary
comparators, which is the most
straightforward method. If you're new to
this channel, don't let this circuit
intimidate you. It's just a combination
of logic gates arranged to produce a
specific result based on its inputs.
In this case, the comparator receives
two binary numbers, A and B, and
produces three outputs.
Only one of these outputs is active at a
time, depending on the inputs. One
output activates if A equals B, another
if A is less than B, and another if A is
greater than B.
We'll abstract this logic into a simple
component called a binary comparator.
We'll use one comparator to check
whether the address is greater than or
equal to the base and another to check
whether it's less than the upper bound.
But remember, both conditions must be
true for the address to be valid. So
we'll combine the outputs using an andgate.
And that's it. The wire at the end of
our circuit will only be active if the
address passed to memory falls within
the address space defined by the base
and limit registers.
But how can a single wire prevent
illegal memory access?
Let's quickly recall how memory works.
Sending an address through the address
bus doesn't trigger a memory operation
on its own. To actually perform a memory
operation, we must specify whether we
want to read from or write to that
address. This is done by activating
either the read enable or write enable signal.
signal.
When read enable is active, memory
places the contents of the address onto
the datab bus. When write enable is
active, memory overwrites the contents
at the specified address using the data
provided on the data bus.
So the simplest way to block illegal
memory access is to use the output of
our address validator circuit to gate
the read and write signals.
That way if an instruction attempts to
access an address outside the process's
address space, the hardware will block
the read or write signal from reaching
the memory module.
In other words, memory will only receive
the read or write enable signal if the
address is within the bounds of the
current processes address space.
Another good idea is to add a few extra
logic gates to detect when a process
attempts to access an illegal address.
That way, the circuitry not only blocks
the memory operation, but also triggers
a hardware signal to the CPU that
immediately interrupts the running
process and invokes the operating
system, which then handles the
violation, most likely by terminating
the process.
On Unix- like systems, when this signal
is caught by the operating system, it
terminates the process and displays the
error message, segmentation fault.
Meaning the process tried to access
memory it wasn't allowed to.
Now, something that should be very clear
at this point is that the operating
system doesn't need to execute any
additional instructions to make this
circuitry work. Because of the way
everything is wired, the circuit
automatically intercepts any memory
operation regardless of its purpose and
immediately performs the validation.
As I'll show in a moment, the only
responsibility of the operating system
is to set the correct values in the base
and limit registers before dispatching
the CPU to the user process.
All this circuitry is of course embedded
inside the CPU.
Just keep in mind that for any of this
to work as expected, the CPU must
support operational modes. These memory
management registers like the base and
limit registers must be implemented as
privileged registers which can only be
modified by the operating system.
This becomes another responsibility of
the operating system during a context
switch. When the CPU is reassigned from
one process to another, the OS not only
captures the state of the interrupted
process and restores the state for the
new process, but also updates the memory
bounds, the base and limit registers to
match the address space of the incoming process.
process.
Once everything is properly set up, the
OS switches the CPU into user mode,
allowing the user process to run. But in
this mode, the CPU cannot modify the
privileged registers. So the process is
limited to using only the memory that
the operating system has explicitly
allocated to it.
And that's the basic idea behind how the
operating system with support from the
hardware prevents user processes from
accessing each other's memory.
We're not done with memory just yet. So
far we've implemented protection for
processes running concurrently. But what
happens when multiple processes are
running and there's no more memory
available for new ones? That's a topic
for a future episode where we'll explore
paging, a memory management technique
that allows a computer to use more
memory than is physically available. So,
make sure to subscribe. You won't want
Click on any text or timestamp to jump to that moment in the video
Share:
Most transcripts ready in under 5 seconds
One-Click Copy125+ LanguagesSearch ContentJump to Timestamps
Paste YouTube URL
Enter any YouTube video link to get the full transcript
Transcript Extraction Form
Most transcripts ready in under 5 seconds
Get Our Chrome Extension
Get transcripts instantly without leaving YouTube. Install our Chrome extension for one-click access to any video's transcript directly on the watch page.
Works with YouTube, Coursera, Udemy and more educational platforms
Get Instant Transcripts: Just Edit the Domain in Your Address Bar!
YouTube
←
→
↻
https://www.youtube.com/watch?v=UF8uR6Z6KLc
YoutubeToText
←
→
↻
https://youtubetotext.net/watch?v=UF8uR6Z6KLc