< Return to Video

Lec 6 | MIT 6.033 Computer System Engineering, Spring 2005

  • 0:00 - 0:00
    So in case you are all worried
    that you may not be in the right
  • 0:04 - 0:04
    class, you are still in 6.033.
    If you're wondering what
  • 0:07 - 0:07
    happened to the Indian guy who
    normally lectures,
  • 0:09 - 0:09
    he's sitting right over there.
    We are trading off lectures
  • 0:13 - 0:13
    throughout the class.
    I'm going to do about the next
  • 0:16 - 0:16
    eight lectures up until spring
    break, and then [Hari?] will be
  • 0:19 - 0:19
    back.
    So, my name is Sam,
  • 0:21 - 0:21
    and feel free to address any
    questions about the lecture to
  • 0:24 - 0:24
    me.
    So today we are going to keep
  • 0:26 - 0:26
    talking about this concept of
    enforcing modularity that we
  • 0:29 - 0:29
    started talking about last time.
  • 0:40 - 0:40
    So last time we saw how we
    could use this notion of the
  • 0:44 - 0:44
    client service model in order to
    separate two modules from each
  • 0:49 - 0:49
    other.
    So the idea was by running the
  • 0:52 - 0:52
    client and the service on
    separate machines,
  • 0:56 - 0:56
    we can isolate these two things
    from each other.
  • 1:00 - 1:00
    So we can make it so,
    for example,
  • 1:02 - 1:02
    when the client wants to invoke
    some operation on the server,
  • 1:07 - 1:07
    that the server can verify the
    request that the client makes,
  • 1:11 - 1:11
    and make sure that the client
    isn't asking to do something
  • 1:15 - 1:15
    malicious.
    Similarly, so this is what we
  • 1:17 - 1:17
    saw last time was this client
    service model.
  • 1:20 - 1:20
    And so the other benefit of the
    client service model was it
  • 1:24 - 1:24
    meant that we could decouple the
    client from the service.
  • 1:28 - 1:28
    So it meant that when the
    client issued a request against
  • 1:32 - 1:32
    the service, it didn't
    necessarily mean that the
  • 1:36 - 1:36
    client, if the service failed
    when the client issued the
  • 1:39 - 1:39
    request, it wasn't necessarily
    the case that the client would
  • 1:44 - 1:44
    also fail.
    So the server might crash
  • 1:48 - 1:48
    executing the request,
    and then the client would get a
  • 1:51 - 1:51
    timeout and would be able to
    retry.
  • 1:53 - 1:53
    And similarly,
    if the client failed,
  • 1:56 - 1:56
    the server could continue
    handling requests on the behalf
  • 1:59 - 1:59
    of other clients.
    OK, so that was a nice
  • 2:02 - 2:02
    separation, the ability to split
    the client and the server apart
  • 2:05 - 2:05
    from each other.
    That's a good thing.
  • 2:07 - 2:07
    But the problem with this
    approach is that we had to have
  • 2:10 - 2:10
    two separate machines.
    The way we presented this was
  • 2:13 - 2:13
    that the client was running on
    one computer,
  • 2:15 - 2:15
    and the server was running on
    another computer.
  • 2:18 - 2:18
    And if you think about this,
    this isn't exactly what we
  • 2:20 - 2:20
    want, right?
    Because that means,
  • 2:22 - 2:22
    suppose we want to build up a
    big, complicated computer system
  • 2:25 - 2:25
    that's composed of multiple
    different modules.
  • 2:27 - 2:27
    If we have to run each one of
    those modules on a separate
  • 2:30 - 2:30
    computer, that's not really very
    ideal, right?
  • 2:34 - 2:34
    To build up a big service,
    we're going to need a whole lot
  • 2:38 - 2:38
    of computers.
    And clearly,
  • 2:40 - 2:40
    that's not the way that
    computer systems that we
  • 2:43 - 2:43
    actually interact with work.
    So what we've seen so far is
  • 2:47 - 2:47
    this notion: we have a module
    per computer.
  • 2:49 - 2:49
    OK, and what we're going to do
    today is see how we can
  • 2:53 - 2:53
    generalize this so that instead
    of having one module per
  • 2:57 - 2:57
    computer, we can instead have
    multiple modules running within
  • 3:01 - 3:01
    a single computer.
    But when we do that,
  • 3:04 - 3:04
    we still want to maintain these
    nice sort of protection benefits
  • 3:08 - 3:08
    that we had between the client
    and service when these two
  • 3:11 - 3:11
    things were running on separate
    computers.
  • 3:14 - 3:14
    OK, so the way we are going to
    do that is by creating something
  • 3:17 - 3:17
    we call a virtual computer.
    So --
  • 3:29 - 3:29
    What we're going to do is we're
    going to create this notion of
  • 3:32 - 3:32
    multiple virtual computers.
    They're all running on one
  • 3:35 - 3:35
    single computer.
    And then we're going to run
  • 3:37 - 3:37
    each one of these modules within
    a virtual computer.
  • 3:40 - 3:40
    OK, and I'll talk more about
    what I mean by a virtual
  • 3:43 - 3:43
    computer throughout the next
    couple of lectures.
  • 3:46 - 3:46
    But the idea is that a virtual
    computer has exactly the same,
  • 3:49 - 3:49
    to a program that's running on
    it, a virtual computers looks
  • 3:52 - 3:52
    just like one of these client or
    server computers might have
  • 3:55 - 3:55
    looked.
    Now, in particular,
  • 3:57 - 3:57
    a virtual computer has the same
    sorts of abstractions that we
  • 4:00 - 4:00
    studied in the previous lectures
    available to it that a real
  • 4:03 - 4:03
    computer has.
    So a virtual computer has a
  • 4:07 - 4:07
    virtual memory --
  • 4:13 - 4:13
    -- and a virtual processor.
    So, today what we're going to
  • 4:18 - 4:18
    talk about is this notion of a
    virtual memory.
  • 4:21 - 4:21
    And so you guys have already
    probably seen the term virtual
  • 4:26 - 4:26
    memory in 6.004.
    So, the word should be familiar
  • 4:30 - 4:30
    to you.
    And we're going to use exactly
  • 4:33 - 4:33
    the same abstraction that we
    used in 6.004.
  • 4:35 - 4:35
    So we're just going to show how
    this abstraction can be used to
  • 4:38 - 4:38
    provide this protection between
    the client and server that we
  • 4:41 - 4:41
    want.
    OK, we're also going to
  • 4:42 - 4:42
    introduce today this notion of
    something called a kernel.
  • 4:45 - 4:45
    And a kernel is something
    that's in charge,
  • 4:47 - 4:47
    basically, of managing all of
    these different virtual
  • 4:50 - 4:50
    computers that are running on
    our one physical computer.
  • 4:53 - 4:53
    So the kernel is going to be
    the sort of system that is going
  • 4:56 - 4:56
    to be the piece of the system
    that actually knows that there
  • 4:59 - 4:59
    are multiple,
    virtual computers running on
  • 5:01 - 5:01
    this system.
    OK, so I want to start off by
  • 5:05 - 5:05
    first just talking about why we
    want a virtualized memory,
  • 5:10 - 5:10
    why virtualizing memory is a
    good thing.
  • 5:13 - 5:13
    So I'm going to abbreviate
    virtual memory as VM.
  • 5:16 - 5:16
    So why would we want to
    virtualize memory?
  • 5:19 - 5:19
    Well, let's see what might
    happen if we build a computer
  • 5:24 - 5:24
    system with multiple modules
    running on it at the same time
  • 5:28 - 5:28
    that didn't have a virtualized
    memory.
  • 5:32 - 5:32
    So suppose we have some
    microprocessor and we have some
  • 5:38 - 5:38
    memory.
    And within this memory,
  • 5:41 - 5:41
    we have the code and data for a
    couple of different modules is
  • 5:48 - 5:48
    stored.
    So we have, say,
  • 5:50 - 5:50
    module A and module B,
    and this is the things like the
  • 5:56 - 5:56
    code and the data that these
    modules are using to currently
  • 6:02 - 6:02
    execute.
    So, in this environment,
  • 6:07 - 6:07
    suppose the module,
    A, executes some instruction.
  • 6:12 - 6:12
    So if you think of this as
    memory, let's say that module A
  • 6:18 - 6:18
    begins at address A here,
    and module B begins at address
  • 6:24 - 6:24
    B here.
    So, suppose that module A
  • 6:27 - 6:27
    executes some instruction like
    store some value,
  • 6:32 - 6:32
    R1, in memory address,
    B.
  • 6:36 - 6:36
    OK, so module A writes in to
    memory address B here.
  • 6:39 - 6:39
    So, this can be a problematic
    thing, right,
  • 6:42 - 6:42
    because if the microprocessor
    just allows this to happen,
  • 6:46 - 6:46
    if it just allows A to write
    into module B's memory,
  • 6:49 - 6:49
    then module B has no way of
    being isolated from module A at
  • 6:53 - 6:53
    all, right?
    Module A can,
  • 6:55 - 6:55
    for example,
    write some sort of garbage into
  • 6:58 - 6:58
    the memory of module B.
    And then when module B tries to
  • 7:02 - 7:02
    read from that memory address,
    it will either,
  • 7:05 - 7:05
    say, try and execute an illegal
    instruction, or it will read in
  • 7:08 - 7:08
    some data that doesn't make any
    sense.
  • 7:11 - 7:11
    So we've lost the separation,
    the isolation that we had
  • 7:14 - 7:14
    between if module A and module B
    are a client and a service
  • 7:17 - 7:17
    trying to interact with each
    other.
  • 7:19 - 7:19
    We've lost this sort of
    ability.
  • 7:21 - 7:21
    We've lost the ability to
    separate and isolate each other,
  • 7:25 - 7:25
    isolate them from each other
    that we had when they were
  • 7:28 - 7:28
    running on separate computers.
    Furthermore,
  • 7:32 - 7:32
    this sort of arrangement of
    virtual memory.
  • 7:35 - 7:35
    So one problem with this is A
    can overwrite B's memory.
  • 7:39 - 7:39
    But we have another problem,
    which is that this sort of
  • 7:43 - 7:43
    arrangement also is very,
    very difficult to debug.
  • 7:46 - 7:46
    So, these kinds of bugs can be
    very hard to track down.
  • 7:50 - 7:50
    And really, when we're building
    computer systems,
  • 7:54 - 7:54
    we'd like to get rid of them.
    So, for example,
  • 7:57 - 7:57
    suppose that A stores into B's
    memory, overwrites an
  • 8:01 - 8:01
    instruction that somewhere sort
    of arbitrarily within B's
  • 8:05 - 8:05
    memory.
    And then, 10,000 instructions
  • 8:09 - 8:09
    later, B tries to load in that
    memory address and execute that
  • 8:12 - 8:12
    instruction.
    And it gets an illegal
  • 8:14 - 8:14
    instruction.
    Now, imagine that you have to
  • 8:17 - 8:17
    debug this program.
    So you sort of are sitting
  • 8:19 - 8:19
    there, and so you run out with
    patient B, and it works just
  • 8:23 - 8:23
    fine.
    And then you run application A,
  • 8:25 - 8:25
    and A overwrites part of B's
    memory.
  • 8:27 - 8:27
    And now, when program B runs
    sometimes long after program A,
  • 8:30 - 8:30
    perhaps, is already terminated,
    B mysteriously crashes on you.
  • 8:35 - 8:35
    So how are you going to track
    down the problem?
  • 8:38 - 8:38
    It's very tricky,
    and it's the kind of problem
  • 8:40 - 8:40
    that's extremely hard to debug
    these kinds of issues.
  • 8:43 - 8:43
    So we really would like to make
    it so that A isn't able to
  • 8:46 - 8:46
    simply overwrite arbitrary
    things into B's memory.
  • 8:49 - 8:49
    We'd like to protect A from B.
    OK, and that's what this
  • 8:52 - 8:52
    virtual memory abstraction that
    we're going to talk about today
  • 8:56 - 8:56
    is going to do for us.
    So, if you think about how you
  • 8:59 - 8:59
    might protect A from B,
    the solution that you sort of
  • 9:02 - 9:02
    come up with after thinking
    about this and staring at this
  • 9:05 - 9:05
    for a little bit is that you
    want to verify that all of the
  • 9:08 - 9:08
    memory accesses that A does are
    actually valid memory accesses
  • 9:12 - 9:12
    that A should be allowed to
    make, so for example,
  • 9:14 - 9:14
    that refer to objects that are
    actually that A has allocated or
  • 9:18 - 9:18
    that A owns.
    OK, so in order to do that,
  • 9:22 - 9:22
    what we're going to need to do
    is to interpose some sort of
  • 9:27 - 9:27
    software in between the memory
    accesses that each module makes,
  • 9:32 - 9:32
    and the actual memory access.
    So the idea is as follows.
  • 9:38 - 9:38
    So this is the virtual memory
    abstraction.
  • 9:44 - 9:44
    OK, so the idea is as follows.
    For each one of our modules,
  • 9:52 - 9:52
    say A and B,
    we're going to create what's
  • 9:57 - 9:57
    known as an address space,
    OK?
  • 10:02 - 10:02
    And this address space is going
    to be, for example,
  • 10:07 - 10:07
    on a 32 bit computer,
    it's two to the 32 minus one,
  • 10:12 - 10:12
    it's going to be a 32 bit
    memory space.
  • 10:15 - 10:15
    So, this is the virtual address
    space of, say,
  • 10:20 - 10:20
    a process, A.
    So, process A can use any
  • 10:23 - 10:23
    memory address that's in this
    two to the 32 bit range,
  • 10:29 - 10:29
    OK?
    But this is going to be a
  • 10:33 - 10:33
    virtual address space.
    And what I mean by that is when
  • 10:40 - 10:40
    process A refers to some address
    within its memory space,
  • 10:47 - 10:47
    call it virtual address BA.
    That address is going to be
  • 10:53 - 10:53
    mapped by the virtual memory
    system into a physical address,
  • 11:01 - 11:01
    OK?
    So this virtual address here is
  • 11:04 - 11:04
    going to be mapped into some
    physical address --
  • 11:13 - 11:13
    -- somewhere within the actual
    physical memory of the system,
  • 11:18 - 11:18
    OK?
    So this physical memory of the
  • 11:21 - 11:21
    system is also a 32 bit space.
    But the sort of mapping from
  • 11:26 - 11:26
    this virtual address in A's
    memory into this physical
  • 11:31 - 11:31
    address space is going to be
    handled by this virtual memory
  • 11:36 - 11:36
    system.
    So now the idea is that each
  • 11:39 - 11:39
    thing that, say,
    a process A or B might want to
  • 11:43 - 11:43
    reference is going to be mapped
    into some different location
  • 11:47 - 11:47
    within this memory.
    So, for example,
  • 11:50 - 11:50
    the code from process A or from
    module A can be mapped into one
  • 11:55 - 11:55
    location, and the code from
    module B is going to be mapped
  • 11:59 - 11:59
    into a different location in the
    physical address space.
  • 12:04 - 12:04
    OK, so we have this notion of
    address spaces.
  • 12:07 - 12:07
    Each module that is running on
    the system in a virtual memory
  • 12:11 - 12:11
    system is going to be allocated
    to one of these address spaces.
  • 12:15 - 12:15
    This address space is going to
    be, and this address space is
  • 12:18 - 12:18
    virtual in the sense that these
    addresses are only valid in the
  • 12:22 - 12:22
    context of this given module,
    OK?
  • 12:24 - 12:24
    And, they translate into,
    each address in the virtual
  • 12:28 - 12:28
    space translates into some
    address in the physical space.
  • 12:32 - 12:32
    Now, if you look at this for a
    minute, you might think,
  • 12:35 - 12:35
    well, this is kind of confusing
    because now B and A both have a
  • 12:38 - 12:38
    32 bit address space.
    And the computer itself only
  • 12:41 - 12:41
    has a 32 bit address space.
    So, there are sort of more
  • 12:44 - 12:44
    addresses between all the
    modules than the computer itself
  • 12:47 - 12:47
    physically has.
    And, if you remember back from
  • 12:49 - 12:49
    6.004, the story that we told
    you about that was that,
  • 12:52 - 12:52
    well, some of these virtual
    addresses can actually be mapped
  • 12:55 - 12:55
    into the disk on the computer.
    So in 6.004,
  • 12:57 - 12:57
    virtual memory was presented as
    a way to create a computer that
  • 13:01 - 13:01
    appeared to have more physical
    memory than it,
  • 13:03 - 13:03
    in fact, did.
    And the way that that was done
  • 13:07 - 13:07
    was to map some of these
    addresses onto disk.
  • 13:10 - 13:10
    So you might have addresses
    from B mapped onto disk.
  • 13:13 - 13:13
    And then, when B tries to
    reference an address that's
  • 13:17 - 13:17
    mapped onto disk,
    the system would load that data
  • 13:20 - 13:20
    from memory, load that data from
    disk and into memory.
  • 13:23 - 13:23
    So, we're not going to talk
    about that aspect of virtual
  • 13:27 - 13:27
    memory very much today.
    The thing to remember is just
  • 13:31 - 13:31
    that these virtual address
    spaces, there may be parts of
  • 13:34 - 13:34
    this virtual address space in
    each of the modules that isn't
  • 13:37 - 13:37
    actually mapped into any
    physical address space.
  • 13:40 - 13:40
    So if one of these modules
    tries to use one of these
  • 13:43 - 13:43
    unmapped virtual address spaces,
    it's not allowed to do that.
  • 13:46 - 13:46
    This virtual memory system will
    signal an error when it tries to
  • 13:49 - 13:49
    do that.
    And on your computer,
  • 13:51 - 13:51
    sometimes you'll see programs
    that will mysteriously crashed
  • 13:54 - 13:54
    with things that say things
    like, tried to access an illegal
  • 13:58 - 13:58
    memory address.
    When it does that,
  • 14:00 - 14:00
    that's because the program try
    to access some memory location
  • 14:03 - 14:03
    that wasn't mapped by the
    virtual memory system.
  • 14:11 - 14:11
    OK, so let's dive a little more
    into actually how this virtual
  • 14:17 - 14:17
    memory abstraction works so we
    can try to understand a little
  • 14:23 - 14:23
    bit more about what's going on.
    So this is going to be a
  • 14:29 - 14:29
    simplified BM hardware,
    OK?
  • 14:32 - 14:32
    It's a little bit simplified
    even from what you learned about
  • 14:36 - 14:36
    in 6.004.
    So, the idea in this simplified
  • 14:39 - 14:39
    hardware is that we have our
    processor.
  • 14:42 - 14:42
    OK, and then we're going to
    have this BM system,
  • 14:45 - 14:45
    which is sometimes called a
    memory management unit,
  • 14:49 - 14:49
    MMU.
    And, this is a piece of
  • 14:50 - 14:50
    hardware that's going to help us
    to do this mapping from these
  • 14:55 - 14:55
    logical addresses in the
    modules' address spaces into the
  • 14:59 - 14:59
    physical memory.
    And then, we're going to have
  • 15:03 - 15:03
    the physical memory,
    OK?
  • 15:05 - 15:05
    Now the idea is that when an
    instruction tries to access some
  • 15:10 - 15:10
    virtual address,
    so for example suppose we
  • 15:14 - 15:14
    execute instruction load some
    virtual address,
  • 15:18 - 15:18
    load into R1 some virtual
    address, OK?
  • 15:21 - 15:21
    What's going to happen when we
    do that is that the
  • 15:25 - 15:25
    microprocessor is going to send
    this virtual address to the VM
  • 15:30 - 15:30
    system.
    And then the VM system is going
  • 15:34 - 15:34
    to translate that into some
    physical address that can be
  • 15:38 - 15:38
    resolved within the memory
    itself.
  • 15:41 - 15:41
    OK, in the way that the virtual
    memory system is going to decide
  • 15:45 - 15:45
    this mapping between a virtual
    address and a physical address
  • 15:50 - 15:50
    is by using something that we
    call a page map,
  • 15:53 - 15:53
    OK?
  • 16:01 - 16:01
    OK, so this table is an
    example.
  • 16:03 - 16:03
    So this is a page map.
    And what a page map basically
  • 16:06 - 16:06
    has is its just a table of
    virtual address to physical
  • 16:09 - 16:09
    address mappings.
    So this is the virtual address
  • 16:12 - 16:12
    to physical address.
    So the idea is that when some
  • 16:15 - 16:15
    virtual address comes in here,
    the virtual memory manager
  • 16:18 - 16:18
    looks up that virtual address in
    this page map,
  • 16:21 - 16:21
    finds the corresponding
    physical address,
  • 16:23 - 16:23
    and then looks that physical
    address up in the actual memory.
  • 16:28 - 16:28
    OK, so now there's one more
    detail that we need,
  • 16:31 - 16:31
    right?
    So what this gives us is we
  • 16:33 - 16:33
    have this notion of a page map
    that does is nothing for us.
  • 16:37 - 16:37
    But we're missing a detail that
    is, OK, what we wanted was for
  • 16:41 - 16:41
    each one of these different
    modules that's running in the
  • 16:44 - 16:44
    system to have a different
    address space associated with
  • 16:48 - 16:48
    it.
    So what we want is we want to
  • 16:50 - 16:50
    have separate page maps for each
    of these different modules,
  • 16:53 - 16:53
    so, for A and B,
    OK, we're going to have a
  • 16:56 - 16:56
    different page map.
    And we're going to have this
  • 16:59 - 16:59
    sort of same,
    we might have multiple copies
  • 17:02 - 17:02
    of a particular virtual address
    in each one of these page maps.
  • 17:07 - 17:07
    And then what we're going to do
    is we're going to allocate a
  • 17:11 - 17:11
    special register on the
    hardware, on the processor.
  • 17:13 - 17:13
    We're going to add a little
    register that's going to allow
  • 17:17 - 17:17
    us to keep track of which one of
    these page maps we are currently
  • 17:21 - 17:21
    looking at, OK?
    So this thing is called the
  • 17:23 - 17:23
    PMAR or the Page Map Address
    Register.
  • 17:35 - 17:35
    OK, and the page map address
    register simply points at one of
  • 17:39 - 17:39
    these page maps.
    OK, so what happens is that the
  • 17:42 - 17:42
    virtual memory system,
    when it wants to resolve a
  • 17:45 - 17:45
    virtual address,
    looks at this page map address
  • 17:48 - 17:48
    register and uses that to find a
    pointer to the beginning of the
  • 17:52 - 17:52
    page map that's currently in
    use.
  • 17:55 - 17:55
    And then he uses the page map
    that's currently in use to
  • 17:58 - 17:58
    resolve, to get what physical
    address corresponds to this
  • 18:02 - 18:02
    logical address.
    OK so this is really the core
  • 18:06 - 18:06
    concept from virtual memory.
    So what we have now is we have
  • 18:10 - 18:10
    this page map address register
    that can be used to select which
  • 18:15 - 18:15
    one of these address spaces we
    are currently using.
  • 18:19 - 18:19
    OK, and so when we have
    selected, for example,
  • 18:22 - 18:22
    the page map for module A,
    then module A can only refer to
  • 18:26 - 18:26
    virtual addresses that are in
    its page map.
  • 18:30 - 18:30
    And those virtual addresses can
    only map into certain physical
  • 18:34 - 18:34
    addresses.
    So, for example,
  • 18:35 - 18:35
    suppose this block here is the
    set of virtual addresses,
  • 18:39 - 18:39
    the set of physical addresses
    that correspond to the virtual
  • 18:43 - 18:43
    addresses in A's page map.
    OK, these are the only physical
  • 18:46 - 18:46
    addresses that A can talk about.
    So if we, for example,
  • 18:50 - 18:50
    have a different block of
    memory addresses that correspond
  • 18:53 - 18:53
    to the virtual addresses that B
    can reference,
  • 18:56 - 18:56
    we can see that there's no way
    for module A to be able to
  • 18:59 - 18:59
    reference any of the memory that
    B uses.
  • 19:03 - 19:03
    So we are able to totally
    separate the physical memory,
  • 19:08 - 19:08
    pieces of physical memory that
    A and B can talk about by using
  • 19:13 - 19:13
    this page map mechanism that
    virtual memory gives us.
  • 19:17 - 19:17
    So, basically what we've done
    is we've sort of added this
  • 19:22 - 19:22
    extra layer of indirection,
    this virtual memory system,
  • 19:26 - 19:26
    that gets to map virtual
    addresses into physical
  • 19:30 - 19:30
    addresses.
    So the rest of this lecture is
  • 19:34 - 19:34
    really going to be details about
    how we make this work,
  • 19:39 - 19:39
    about how we actually decide,
    how we assign this PMAR
  • 19:43 - 19:43
    register based on which one of
    the modules is currently
  • 19:47 - 19:47
    executing about things like what
    the format of this page map
  • 19:52 - 19:52
    table is going to look like,
    OK?
  • 19:54 - 19:54
    So this is really the key
    concept.
  • 19:57 - 19:57
    So what I want to do now is
    sort of turn to this second
  • 20:01 - 20:01
    question I asked which is,
    how does this page map thing
  • 20:06 - 20:06
    actually work?
    How is it actually represented?
  • 20:10 - 20:10
    So one very simple
    representation of a page map
  • 20:13 - 20:13
    might be that it simply is a
    pointer to, the page map just
  • 20:18 - 20:18
    says where A's memory begins on
    the processor,
  • 20:21 - 20:21
    right?
    So it's just one value.
  • 20:23 - 20:23
    It says A's memory begins at
    this location in the physical
  • 20:27 - 20:27
    memory, and all virtual
    addresses should be resolved
  • 20:31 - 20:31
    relative to this beginning
    location of A's memory.
  • 20:35 - 20:35
    The problem with that
    representation is that it's not
  • 20:39 - 20:39
    very flexible.
    So, for example,
  • 20:42 - 20:42
    suppose there's a third module,
    C, which is laid out in memory
  • 20:47 - 20:47
    right next to A.
    So, its storage is placed right
  • 20:50 - 20:50
    next to A in memory.
    And now, suppose that A wants
  • 20:54 - 20:54
    to allocate some additional
    memory that it can use.
  • 20:58 - 20:58
    Now, in order to do that,
    if the page map is simply a
  • 21:02 - 21:02
    single pointer to the beginning
    of A's address space,
  • 21:07 - 21:07
    we're kind of in trouble.
    We can't just add memory onto
  • 21:12 - 21:12
    the bottom because then we would
    overlap C.
  • 21:14 - 21:14
    So we're going to have to move
    all of A's memory around in
  • 21:17 - 21:17
    order to be able to make space
    for this.
  • 21:20 - 21:20
    OK, so this seems like a little
    bit problematic simply have it
  • 21:23 - 21:23
    be a pointer.
    The other thing we could do is
  • 21:26 - 21:26
    suppose we could,
    instead, have a different
  • 21:28 - 21:28
    option where we could say,
    for example,
  • 21:30 - 21:30
    for every virtual address in
    A's address space,
  • 21:33 - 21:33
    so for each 32 bit value that A
    wants to resolve,
  • 21:35 - 21:35
    there might be an entry in this
    page map table,
  • 21:38 - 21:38
    right?
    So for every 32-bit virtual
  • 21:40 - 21:40
    address, there would be a
    corresponding 32-bit physical
  • 21:43 - 21:43
    address.
    And there would just be a
  • 21:46 - 21:46
    one-to-one mapping between all
    these things.
  • 21:48 - 21:48
    So, if A could reference a
    million blocks of memory,
  • 21:51 - 21:51
    there would be a million
    entries in this page map table.
  • 21:54 - 21:54
    So, if you think about this for
    a minute, that sounds like kind
  • 21:57 - 21:57
    of a bad idea,
    too, because now these tables
  • 21:59 - 21:59
    are totally huge,
    right, and in fact they are
  • 22:02 - 22:02
    almost as big as the memory
    itself, right,
  • 22:04 - 22:04
    because if I have a million
    entries, if A can reference a
  • 22:07 - 22:07
    million blocks,
    then I'm going to need a
  • 22:09 - 22:09
    million entries in the table.
    So the table becomes just as
  • 22:14 - 22:14
    big as the memory itself.
    So we need some in between sort
  • 22:19 - 22:19
    of alternative hybrid between
    these two extremes.
  • 22:24 - 22:24
    And the idea,
    again, is very simple.
  • 22:27 - 22:27
    And you saw it in 6.004.
    So the idea is to take this
  • 22:32 - 22:32
    32-bit virtual address.
    So suppose this is our 32-bit
  • 22:38 - 22:38
    virtual address.
    Now what we're going to do is
  • 22:41 - 22:41
    we're going to split it up into
    two pieces, a page number and an
  • 22:47 - 22:47
    offset.
    OK, and we're going to choose
  • 22:50 - 22:50
    some size for these two things.
    For now I'll just arbitrarily
  • 22:55 - 22:55
    pick a 20 bit page number and a
    12 bit offset.
  • 23:00 - 23:00
    OK, so what this is going to
    do, so now what we're going to
  • 23:05 - 23:05
    do is instead of storing a
    single word of memory at each
  • 23:10 - 23:10
    entry in this table,
    we're going to store a page of
  • 23:15 - 23:15
    memory at each entry in this
    table.
  • 23:18 - 23:18
    So --
  • 23:29 - 23:29
    So this table is going to look
    like a mapping between a page,
  • 23:35 - 23:35
    and a physical address.
    OK, so what a page is,
  • 23:40 - 23:40
    so if the page number is 20
    bits long, then that means that
  • 23:46 - 23:46
    each page is going to be two to
    the 12th bits big,
  • 23:51 - 23:51
    which is equal to,
    say, 4,096 words,
  • 23:54 - 23:54
    OK?
    So the idea is that we're going
  • 23:58 - 23:58
    to have two to the 20th pages
    within each address space,
  • 24:04 - 24:04
    and each page is going to map
    to one of these 4,096 byte
  • 24:09 - 24:09
    blocks, OK?
    So, if we have our memory here
  • 24:16 - 24:16
    this page, say,
    page one is going to map into
  • 24:21 - 24:21
    some physical address.
    And page two is going to map
  • 24:27 - 24:27
    into some other physical block,
    OK, so each one of these things
  • 24:35 - 24:35
    is now 4,096 bytes,
    each block here,
  • 24:40 - 24:40
    OK?
    And so this,
  • 24:42 - 24:42
    let's just expand this.
    So this is now a page.
  • 24:46 - 24:46
    And this 12 bit offset is going
    to be used in order to look up
  • 24:50 - 24:50
    the word that we want to
    actually look up in this page.
  • 24:55 - 24:55
    So, if the virtual address is,
    say, for example,
  • 24:59 - 24:59
    page one offset zero,
    what that's going to do is
  • 25:02 - 25:02
    we're going to look up in the
    page map.
  • 25:05 - 25:05
    We're going to find the page
    number that corresponds to,
  • 25:10 - 25:10
    we're going to find page number
    one.
  • 25:14 - 25:14
    We're going to find the
    physical address that
  • 25:17 - 25:17
    corresponds to it,
    we're going to go down here,
  • 25:20 - 25:20
    and look at this block of
    memory.
  • 25:22 - 25:22
    And then within this 4,096
    block memory,
  • 25:25 - 25:25
    we're going to take the address
    zero, the first word within that
  • 25:29 - 25:29
    thing, OK?
    So now the size of these page
  • 25:32 - 25:32
    tables is much smaller,
    right?
  • 25:34 - 25:34
    They're no longer two to the
    30th bits.
  • 25:37 - 25:37
    Now they're two to the 20th
    bits, which is some small number
  • 25:42 - 25:42
    of megabytes big.
    But we've avoided this problem
  • 25:45 - 25:45
    that we have before.
    We have some flexibility in
  • 25:49 - 25:49
    terms of how each page maps into
    the physical memory.
  • 25:53 - 25:53
    So I can allocate a third page
    to a process.
  • 25:56 - 25:56
    And I can map that into any
    4,096 byte block that's in
  • 26:00 - 26:00
    memory that's currently unused.
    OK, so I have flexibility.
  • 26:06 - 26:06
    I don't have this problem,
    say, where A and C were
  • 26:09 - 26:09
    colliding with each other.
  • 26:19 - 26:19
    OK, so this is sort of the
    outline of how virtual memory
  • 26:23 - 26:23
    works.
    But what I haven't yet
  • 26:25 - 26:25
    described to you is how it is
    that we can actually go about
  • 26:30 - 26:30
    creating these different address
    spaces that are allocated to the
  • 26:34 - 26:34
    different modules,
    and how we can switch between
  • 26:38 - 26:38
    different modules using this
    PMAR register.
  • 26:42 - 26:42
    So I've sort of described this
    as, suppose these data
  • 26:45 - 26:45
    structures exist,
    now here's how we can use them.
  • 26:48 - 26:48
    But I haven't told you how
    these data structures all get
  • 26:51 - 26:51
    together, and created,
    and set up to begin with.
  • 26:54 - 26:54
    And I hinted at this in the
    beginning.
  • 26:56 - 26:56
    But in order to do this,
    what we're going to need is
  • 26:59 - 26:59
    some sort of a special
    supervisory module that's able
  • 27:02 - 27:02
    to look at the page maps for all
    of these different,
  • 27:05 - 27:05
    that's able to create new page
    maps and look at the page maps
  • 27:08 - 27:08
    for all of the different modules
    that are within the system.
  • 27:13 - 27:13
    And the supervisory module is
    going to be able to do things
  • 27:17 - 27:17
    like add new memory to a page
    map or be able to destroy,
  • 27:21 - 27:21
    delete a particular module and
    its associated page map.
  • 27:24 - 27:24
    So we need some sort of a thing
    that can manage all this
  • 27:28 - 27:28
    infrastructure.
    So, this supervisory module --
  • 27:37 - 27:37
    -- is called the kernel.
    OK, and the kernel is really,
  • 27:41 - 27:41
    it's going to be the thing
    that's going to be in charge of
  • 27:46 - 27:46
    managing all these data
    structures for us.
  • 27:49 - 27:49
    So --
  • 27:58 - 27:58
    So here's our microprocessor
    with its PMAR register on it.
  • 28:03 - 28:03
    And, what we're going to do is
    we're going to extend the
  • 28:07 - 28:07
    microprocessor with one
    additional piece of hardware,
  • 28:11 - 28:11
    and this is the user kernel
    bit, OK?
  • 28:14 - 28:14
    So, this is just a bit
    specifies whether we are
  • 28:17 - 28:17
    currently running a user module
    that is just a program that's
  • 28:22 - 28:22
    running on your computer,
    or whether the kernel is
  • 28:26 - 28:26
    currently executed,
    OK?
  • 28:29 - 28:29
    And the idea with this kernel
    bit is that when this kernel bit
  • 28:35 - 28:35
    is set, the code that is running
    is going to have special
  • 28:40 - 28:40
    privileges.
    It's going to be able to
  • 28:44 - 28:44
    manipulate special things about
    the hardware and the processor.
  • 28:50 - 28:50
    And in particular,
    we're going to have a rule that
  • 28:54 - 28:54
    says that only the kernel can
    change the PMAR,
  • 28:59 - 28:59
    OK?
    So the PMAR is the thing that
  • 29:02 - 29:02
    specifies which process is
    currently running,
  • 29:04 - 29:04
    and selects which address space
    we want to be currently using.
  • 29:07 - 29:07
    And what we're going to use is
    we're going to use,
  • 29:10 - 29:10
    so what we're going to do is
    have the kernel be the thing
  • 29:13 - 29:13
    that's in charge of manipulating
    the value of this PMAR register
  • 29:17 - 29:17
    to select which thing is
    currently being executed.
  • 29:19 - 29:19
    And we want and they get so
    that only the kernel can do this
  • 29:23 - 29:23
    because if we,
    for example,
  • 29:24 - 29:24
    allowed one of these other
    programs to be able to
  • 29:27 - 29:27
    manipulate the PMAR,
    right, then that other program
  • 29:29 - 29:29
    might be able to do something
    unpleasant to the computer,
  • 29:32 - 29:32
    right?
    It changes the PMAR to point at
  • 29:36 - 29:36
    some other program's memory.
    And now, suddenly all the
  • 29:39 - 29:39
    memory addresses in the system
    are going to be resolved.
  • 29:42 - 29:42
    Then suddenly,
    we are going to be sort of
  • 29:44 - 29:44
    resolving memory addresses
    relative to some other module's
  • 29:47 - 29:47
    page map.
    And that's likely to be a
  • 29:49 - 29:49
    problematic thing.
    It's likely to cause that other
  • 29:52 - 29:52
    module to crash,
    for example,
  • 29:53 - 29:53
    because the processor is set up
    to be executing instructions
  • 29:56 - 29:56
    from the current program.
    So we want to make sure that
  • 29:59 - 29:59
    only something this kernel can
    change the PMAR.
  • 30:03 - 30:03
    And this kernel is going to be
    this sort of supervisory module
  • 30:07 - 30:07
    that all of the other modules
    are going to have to trust to
  • 30:10 - 30:10
    kind of do the right thing and
    manage the computer's execution
  • 30:14 - 30:14
    for you.
    And this kernel,
  • 30:15 - 30:15
    except for this one difference
    that a kernel can change the
  • 30:19 - 30:19
    PMAR, the kernel is,
    in all other respects,
  • 30:21 - 30:21
    essentially just going to be
    another module that's running in
  • 30:25 - 30:25
    the computer system.
    So in particular,
  • 30:27 - 30:27
    the kernel is also going to
    have one of these 32-bit virtual
  • 30:31 - 30:31
    address spaces associated with
    it, OK?
  • 30:34 - 30:34
    But, what we're going to do is
    we're going to say that the
  • 30:38 - 30:38
    kernel within its address space
    has all of the page maps of all
  • 30:42 - 30:42
    the other programs that are
    currently running on the system
  • 30:47 - 30:47
    mapped into its address space.
    OK, so this is a little bit
  • 30:51 - 30:51
    tricky because I presented this
    as though A and B referenced
  • 30:55 - 30:55
    totally different pieces of
    memory.
  • 30:57 - 30:57
    So, I sort of have told you so
    far only about modules that are
  • 31:01 - 31:01
    referencing disjoint sets of
    memory.
  • 31:05 - 31:05
    But in fact these page maps,
    right, they just reside in
  • 31:09 - 31:09
    memory somewhere.
    And it's just fine if I,
  • 31:12 - 31:12
    for example,
    have multiple modules that are
  • 31:15 - 31:15
    able to reference,
    that have the same physical
  • 31:18 - 31:18
    addresses mapped into their
    virtual address space.
  • 31:22 - 31:22
    So it's very likely that I
    might want to have something
  • 31:25 - 31:25
    down here at the bottom that
    both A and B can access.
  • 31:30 - 31:30
    So, this might be stuff that's
    shared between A and B.
  • 31:33 - 31:33
    I can map that into both A and
    B's memory.
  • 31:36 - 31:36
    In the same way,
    what I'm going to do is I'm
  • 31:39 - 31:39
    going to map these page maps,
    which are also stored in memory
  • 31:42 - 31:42
    into all of the page maps into
    the kernel's address space.
  • 31:46 - 31:46
    So the kernel is going to be
    able to reference the address
  • 31:49 - 31:49
    spaces of all the modules.
    And the kernel is also going to
  • 31:53 - 31:53
    keep a little table of all the
    page maps for all of the
  • 31:56 - 31:56
    currently running programs on
    the system.
  • 32:00 - 32:00
    So these are,
    for example,
  • 32:01 - 32:01
    page maps for A and B.
    And this is a list of all the
  • 32:04 - 32:04
    maps that are currently running
    in the system.
  • 32:07 - 32:07
    So what's going to happen,
    now what can happen is because
  • 32:10 - 32:10
    the kernel is allowed to change
    the PMAR, and because it knows
  • 32:14 - 32:14
    about the location of all the
    other address spaces that are in
  • 32:18 - 32:18
    the system, when it wants to
    start running one of these
  • 32:21 - 32:21
    programs that's running in the
    system, it can change the value
  • 32:24 - 32:24
    of the PMAR to be sort of the
    PMAR for A or the PMAR to B.
  • 32:28 - 32:28
    And, it can manipulate all the
    values of the registers in the
  • 32:31 - 32:31
    system so that you can start
    executing code for one of these.
  • 32:35 - 32:35
    You can switch between one of
    these two modules.
  • 32:39 - 32:39
    So the actual process of
    switching between which module
  • 32:42 - 32:42
    is currently running.
    We're going to focus on that
  • 32:44 - 32:44
    more next time.
    So, don't worry too much if you
  • 32:47 - 32:47
    don't understand the details of
    how you actually switch from
  • 32:50 - 32:50
    executing one program to another
    program.
  • 32:52 - 32:52
    But, you can see that the
    kernel can switch which address
  • 32:55 - 32:55
    space is currently active by
    simply manipulating the value of
  • 32:58 - 32:58
    this PMAR register.
    Furthermore,
  • 33:01 - 33:01
    the kernel can do things like
    it can create a new map.
  • 33:04 - 33:04
    So the kernel can simply
    allocate one of these new
  • 33:07 - 33:07
    tables, and it can set up a
    mapping from a set of virtual
  • 33:11 - 33:11
    addresses to a set of physical
    addresses so that you can create
  • 33:15 - 33:15
    a new address space that a new
    module can start executing
  • 33:19 - 33:19
    within.
    Similarly, the kernel can do
  • 33:21 - 33:21
    things like allocate new memory
    into one of these addresses.
  • 33:25 - 33:25
    So it can map some additional
    virtual addresses into real
  • 33:29 - 33:29
    physical memory so that when one
    of these modules,
  • 33:32 - 33:32
    say for example,
    requests additional memory to
  • 33:35 - 33:35
    execute, the kernel can add that
    memory, add an entry into the
  • 33:39 - 33:39
    table so that that new memory
    that the program has requested
  • 33:42 - 33:42
    actually maps into a valid,
    physical address.
  • 33:47 - 33:47
    OK, so the question,
    of course, then is,
  • 33:50 - 33:50
    so you can see the value of
    having this kernel module.
  • 33:54 - 33:54
    But the question is,
    how do we communicate between
  • 33:58 - 33:58
    these modules that are running
    these user level modules that
  • 34:02 - 34:02
    are running, and the kernel
    module that the user modules
  • 34:07 - 34:07
    need to invoke,
    for example,
  • 34:09 - 34:09
    request new memory.
    So the way that we are going to
  • 34:13 - 34:13
    do this is just like we've done
    everything else in this lecture
  • 34:16 - 34:16
    so far: by adding a little bit
    of extra, by changing the
  • 34:20 - 34:20
    processor just a little bit.
    So in particular,
  • 34:22 - 34:22
    what we're going to do --
  • 34:37 - 34:37
    What we're going to do is to is
    to add this notion of a
  • 34:42 - 34:42
    supervisor call.
    Call that SDC.
  • 34:45 - 34:45
    So a supervisor call is simply
    a special instruction that is on
  • 34:50 - 34:50
    the processor that invokes the
    kernel.
  • 34:54 - 34:54
    So when the supervisor call
    instruction gets executed,
  • 34:58 - 34:58
    it's going to set up the state
    of these PMAR and user kernel
  • 35:04 - 35:04
    bit instructions so that the
    kernel can begin executing.
  • 35:10 - 35:10
    The supervisor call is also
    going.
  • 35:12 - 35:12
    But when the supervisor call
    instruction is executed,
  • 35:16 - 35:16
    we need to be a low but careful
    because we also need to decide
  • 35:21 - 35:21
    somehow which code within the
    kernel we want to begin
  • 35:25 - 35:25
    executing when the supervisor
    call instruction gets executed,
  • 35:29 - 35:29
    right?
    So what the supervisor call
  • 35:31 - 35:31
    instruction does is it accepts a
    parameter which is the name of a
  • 35:36 - 35:36
    so-called gate function.
    So a gate function is a well
  • 35:41 - 35:41
    defined entry point into another
    module that can be used to
  • 35:46 - 35:46
    invoke a piece of code in that
    other module.
  • 35:49 - 35:49
    So, for example,
    the kernel is going to have a
  • 35:53 - 35:53
    particular gate function which
    corresponds to allocating
  • 35:57 - 35:57
    additional memory for a module.
    So when a user program executes
  • 36:04 - 36:04
    an instruction,
    supervisor call,
  • 36:08 - 36:08
    to gate say for example,
    this might be some memory
  • 36:14 - 36:14
    allocation code,
    this special instruction is
  • 36:19 - 36:19
    going to do the following
    things.
  • 36:23 - 36:23
    First it's going to set the
    user kernel bit to kernel.
  • 36:30 - 36:30
    Then it's going to set the
    value of the PMAR register to
  • 36:37 - 36:37
    the kernel page map.
    It's going to save the program
  • 36:44 - 36:44
    counter for the currently
    executing instruction,
  • 36:48 - 36:48
    and then it's going to set the
    program counter to be the
  • 36:53 - 36:53
    address of the gate function,
    OK?
  • 36:55 - 36:55
    So we've introduced a special
    new processor instruction that
  • 37:01 - 37:01
    takes these steps.
    So when this instruction is
  • 37:05 - 37:05
    executed, we essentially switch
    into executing within the
  • 37:10 - 37:10
    kernel's address space.
    OK, and this kernel,
  • 37:14 - 37:14
    N, we begin executing within
    the kernel address space at the
  • 37:20 - 37:20
    address of this gate function
    within the kernel's address
  • 37:25 - 37:25
    space.
    OK, so what this has done is
  • 37:28 - 37:28
    this is a well defined entry
    point.
  • 37:32 - 37:32
    If the program tries to execute
    this instruction with the name
  • 37:35 - 37:35
    of a gate function that doesn't
    exist, then that program is
  • 37:38 - 37:38
    going to get an error when it
    tries to do that.
  • 37:41 - 37:41
    So the program can only name
    gate functions that actually
  • 37:44 - 37:44
    correspond to real things that
    the operating system can do,
  • 37:47 - 37:47
    that the kernel can do.
    And so, the kernel is then
  • 37:50 - 37:50
    going to be invoked and take
    that action that was requested
  • 37:53 - 37:53
    of it.
    On return, essentially,
  • 37:54 - 37:54
    so when the kernel finishes
    executing this process,
  • 37:57 - 37:57
    when it finishes executing
    this, say, memory allocation
  • 38:00 - 38:00
    instruction, we are just going
    to reverse this step.
  • 38:04 - 38:04
    So we are just going to set the
    PMAR to be, we're going to set
  • 38:08 - 38:08
    the PMAR back to the user's
    program.
  • 38:10 - 38:10
    We are going to set the user
    kernel bit back into user mode.
  • 38:14 - 38:14
    And then we're going to jump
    back to the saved return address
  • 38:18 - 38:18
    that we saved here,
    the saved program counter
  • 38:21 - 38:21
    address from the user's program.
    So when the program finishes,
  • 38:25 - 38:25
    when the kernel finishes
    executing this service
  • 38:28 - 38:28
    instruction, the control will
    just return back to the program
  • 38:32 - 38:32
    in place where the program needs
    to begin executing.
  • 38:36 - 38:36
    OK, so now using this gate,
    so using this notion of,
  • 38:40 - 38:40
    so what we've seen so far now
    is the ability for the,
  • 38:44 - 38:44
    we can use the virtual memory
    abstraction in order to protect
  • 38:49 - 38:49
    the memory references of two
    modules from each other.
  • 38:53 - 38:53
    And we see when we have this
    notion of the kernel that can be
  • 38:58 - 38:58
    used to, for example,
    that can be used to manage
  • 39:01 - 39:01
    these address spaces to allocate
    new memory to these address
  • 39:06 - 39:06
    spaces, and in general,
    to sort of manage these address
  • 39:10 - 39:10
    spaces.
    So this kernel is also going to
  • 39:15 - 39:15
    allow us to do exactly what we
    set out trying to do,
  • 39:21 - 39:21
    which is to act as the
    so-called trusted intermediary
  • 39:26 - 39:26
    between, say for example,
    a client and a server running
  • 39:32 - 39:32
    on the same machine.
  • 39:40 - 39:40
    OK, so I trust that
    intermediary is just a piece of
  • 39:43 - 39:43
    code.
    So suppose we have a client and
  • 39:45 - 39:45
    a server, right,
    and these are two pieces of
  • 39:47 - 39:47
    code written by two different
    developers.
  • 39:49 - 39:49
    Maybe the two developers don't
    necessarily trust that the other
  • 39:52 - 39:52
    developer has written a piece of
    code that's 100% foolproof
  • 39:55 - 39:55
    because it doesn't have any
    bugs.
  • 39:57 - 39:57
    But both of those developers
    may be willing to say that they
  • 40:00 - 40:00
    will trust that the kernel is
    properly written and doesn't
  • 40:03 - 40:03
    have any bugs in it.
    And so they are willing to
  • 40:07 - 40:07
    allow the kernel to sit in
    between those two programs and
  • 40:12 - 40:12
    make sure that neither of them
    has any bad interactions with
  • 40:17 - 40:17
    each other.
    So let's see how we can use
  • 40:20 - 40:20
    this kernel, the kernel combined
    with virtual memory in order to
  • 40:25 - 40:25
    be able to do this.
    So suppose this is our kernel.
  • 40:30 - 40:30
    So the idea is that this kernel
    running has, so suppose we have
  • 40:33 - 40:33
    [sue?] processes A and B that
    want to communicate with each
  • 40:37 - 40:37
    other un some way.
    And, we already said that we
  • 40:39 - 40:39
    don't want these two processes
    to be able to directly reference
  • 40:42 - 40:42
    into each other's memory because
    that makes them dependent on
  • 40:46 - 40:46
    each other.
    It means that if there's a bug
  • 40:48 - 40:48
    in A, that A can overwrite some
    memory in B's address space and
  • 40:51 - 40:51
    cause B to [NOISE OBSCURES].
    So, that's' a bad thing.
  • 40:54 - 40:54
    So instead, what we are going
    to do is we're going to create,
  • 40:57 - 40:57
    well, one thing we can do is to
    create a special set of these
  • 41:01 - 41:01
    supervisor calls that these two
    modules can use to interact with
  • 41:04 - 41:04
    each other.
    So in particular,
  • 41:07 - 41:07
    we might within the kernel
    maintain a queue of messages
  • 41:11 - 41:11
    that these two programs are
    exchanging with each other,
  • 41:16 - 41:16
    a list of messages that they're
    exchanging.
  • 41:19 - 41:19
    And then, suppose A is,
    we're going to call this guy,
  • 41:23 - 41:23
    A is a producer.
    He's creating messages.
  • 41:26 - 41:26
    And B is a consumer.
    A can call some function like
  • 41:30 - 41:30
    Put which will supervise their
    call like Put which will cause a
  • 41:34 - 41:34
    data item to be put on this
    queue.
  • 41:38 - 41:38
    And then sometime later,
    B can call this supervisor call
  • 41:41 - 41:41
    Get, and pull this value out of
    the queue, OK?
  • 41:43 - 41:43
    So in this way,
    the producer and the consumer
  • 41:46 - 41:46
    can interact with each other.
    They can exchange data with
  • 41:49 - 41:49
    each other and because we have
    this gate interface,
  • 41:51 - 41:51
    this well-defined gate
    interface with these Put and Get
  • 41:54 - 41:54
    calls being invoked by the
    kernel, the kernel can be very
  • 41:57 - 41:57
    careful, just like in the case
    of the client and server running
  • 42:01 - 42:01
    on two different machines.
    In this case,
  • 42:04 - 42:04
    the kernel can carefully verify
    that these put and get commands
  • 42:08 - 42:08
    that these two different modules
    are calling actually are
  • 42:12 - 42:12
    correctly formed,
    that the parameters are valid,
  • 42:15 - 42:15
    that they're referring to valid
    locations in memory.
  • 42:18 - 42:18
    And therefore,
    the kernel can sort of ensure
  • 42:21 - 42:21
    that these two modules don't do
    malicious things to each other
  • 42:25 - 42:25
    or cause each other to break.
    So this is an example here of
  • 42:29 - 42:29
    something that's like an
    inter-process communication.
  • 42:33 - 42:33
    So you saw, for example,
    you've seen an instance of
  • 42:36 - 42:36
    inter-process communication when
    he studied the UNIX paper.
  • 42:39 - 42:39
    We talked about pipes,
    and a pipe abstraction,
  • 42:42 - 42:42
    and how that works.
    Well, pipes are sort of
  • 42:44 - 42:44
    something that's implemented by
    the kernel in UNIX as a way for
  • 42:48 - 42:48
    two programs to exchange data
    with each other.
  • 42:50 - 42:50
    And, there's lots of these
    kinds of services that our
  • 42:53 - 42:53
    people tend to push into the
    kernel that the kernel provides
  • 42:57 - 42:57
    to the other applications,
    the modules that are running,
  • 43:00 - 43:00
    so that these modules can,
    for example,
  • 43:02 - 43:02
    interact with hardware or
    interact with each other.
  • 43:06 - 43:06
    So commonly within a kernel,
    you'd find things like a file
  • 43:09 - 43:09
    system, an interface to the
    network, and you might,
  • 43:12 - 43:12
    for example,
    find things like an interface
  • 43:15 - 43:15
    to the graphics hardware.
    OK, there is some sort of
  • 43:18 - 43:18
    comment so if you look at what's
    actually within a kernel,
  • 43:21 - 43:21
    there is a huge amount of code
    that's going into these kernels.
  • 43:24 - 43:24
    So I think we talked earlier
    about how the Linux operating
  • 43:28 - 43:28
    system is many millions of lines
    of code.
  • 43:31 - 43:31
    If you go look at the Linux
    kernel, the Linux kernel is
  • 43:34 - 43:34
    probably today on the order of
    about 5 million lines of code,
  • 43:38 - 43:38
    most of which,
    say two thirds of which,
  • 43:40 - 43:40
    is related to these so-called
    device drivers that manage this
  • 43:43 - 43:43
    low-level hardware.
    So this kernel has gotten quite
  • 43:46 - 43:46
    large.
    And one of the side effects of
  • 43:49 - 43:49
    the kernel getting large is that
    maybe it's harder to trust it,
  • 43:52 - 43:52
    right?
    May be you sort of have less
  • 43:54 - 43:54
    confidence that all the code in
    the kernel is actually correct.
  • 43:58 - 43:58
    And you can imagine that if you
    don't trust the kernel then the
  • 44:02 - 44:02
    computer is not going to be as
    stable as you like to be.
  • 44:06 - 44:06
    And this is one argument for
    why Windows crashes all the time
  • 44:09 - 44:09
    is because it has all these
    drivers in it and these drivers
  • 44:13 - 44:13
    aren't necessarily all perfectly
    written.
  • 44:15 - 44:15
    There are tens of millions of
    lines of code in Windows,
  • 44:18 - 44:18
    and some of them crash some of
    the time, and that causes the
  • 44:21 - 44:21
    whole computer to come down.
    So there's a tension in the
  • 44:24 - 44:24
    operating systems community
    about whether you should execute
  • 44:27 - 44:27
    these things,
    you should keep these things
  • 44:30 - 44:30
    look the file system or graphics
    system within the kernel or
  • 44:33 - 44:33
    whether you should move them
    outside as separate services,
  • 44:36 - 44:36
    which can be invoked in the
    same way, for example,
  • 44:39 - 44:39
    that A and B interact with each
    other, by having some data
  • 44:42 - 44:42
    structure that's stored within
    the kernel that buffers the
  • 44:45 - 44:45
    requests between,
    say, the service and this,
  • 44:47 - 44:47
    say, for example the graphics
    service and the users' programs
  • 44:51 - 44:51
    that want to interact with it.
    OK, so that's basically it for
  • 44:57 - 44:57
    today.
    What I've shown you is,
  • 44:59 - 44:59
    well, actually we have a few
    more minutes.
  • 45:03 - 45:03
    Sorry.
    [LAUGHTER] I know you're all
  • 45:06 - 45:06
    getting up to leave,
    but so, OK, I just want to
  • 45:11 - 45:11
    quickly touch on one last topic
    which is that,
  • 45:16 - 45:16
    so, what I've shown you so far
    is how we can use the notion of
  • 45:22 - 45:22
    virtual memory in order to
    protect the data,
  • 45:26 - 45:26
    protect two programs from each
    other so that they can't
  • 45:31 - 45:31
    necessarily interact with each
    other's data.
  • 45:37 - 45:37
    But there is some situations in
    which we might actually want to
  • 45:41 - 45:41
    have two programs able to share
    some data with each other.
  • 45:44 - 45:44
    So I don't know if you guys
    remember, but when Hari was
  • 45:47 - 45:47
    lecturing earlier,
    he talked about how there are
  • 45:50 - 45:50
    libraries that get linked into
    programs.
  • 45:52 - 45:52
    And one of the common ways that
    libraries are structured these
  • 45:56 - 45:56
    days is a so-called shared
    library.
  • 45:58 - 45:58
    So a shared library is
    something that is only stored in
  • 46:01 - 46:01
    one location in physical memory.
    But multiple different modules
  • 46:06 - 46:06
    that are executing on that
    system can call functions within
  • 46:09 - 46:09
    that shared library.
    So in order to make this work,
  • 46:12 - 46:12
    right, we need to have mapped
    the memory for the shared
  • 46:15 - 46:15
    library that has all the
    functions that these other
  • 46:18 - 46:18
    modules want to call into the
    address spaces for both of these
  • 46:22 - 46:22
    modules so that they can
    actually execute the code that's
  • 46:25 - 46:25
    there.
    So the virtual memory system
  • 46:27 - 46:27
    makes it very trivial to do
    this.
  • 46:30 - 46:30
    So suppose I have my address
    space for some function,
  • 46:35 - 46:35
    A, and I have my address space
    for some function,
  • 46:40 - 46:40
    B.
    And suppose that function A
  • 46:43 - 46:43
    references library one and
    module B references libraries
  • 46:48 - 46:48
    one and two.
    OK, so using the virtual memory
  • 46:53 - 46:53
    system, suppose we have,
    so this is our physical memory.
  • 46:58 - 46:58
    So, suppose that module A,
    program A, is loaded.
  • 47:04 - 47:04
    And when it's loaded,
    the program that runs other
  • 47:08 - 47:08
    programs, the loader in this
    case, is going to load this
  • 47:13 - 47:13
    shared library,
    one, as it loads for program A.
  • 47:17 - 47:17
    So, it's going to first load
    the code for A,
  • 47:20 - 47:20
    and then it's going to load the
    code for one.
  • 47:24 - 47:24
    And, these things are going to
    be sitting in memory somewhere.
  • 47:30 - 47:30
    So, within A's address space
    we're going to have the code for
  • 47:34 - 47:34
    A is going to be mapped and the
    code for one is going to be
  • 47:37 - 47:37
    mapped.
    Now, when B executes,
  • 47:39 - 47:39
    right, what we want to avoid,
    so the whole purpose of shared
  • 47:43 - 47:43
    libraries is to make it so that
    when two programs are running
  • 47:46 - 47:46
    and they both use the same
    library there aren't two copies
  • 47:50 - 47:50
    of that library that are in
    memory using twice as much of
  • 47:53 - 47:53
    the memory of,
    right, because there's all
  • 47:56 - 47:56
    these libraries that all
    computer programs share.
  • 48:00 - 48:00
    For example,
    on Linux there's the [LIB C?]
  • 48:03 - 48:03
    library that implements all of
    the standard functions that
  • 48:07 - 48:07
    people use in C programs.
    If there's 50 programs written
  • 48:10 - 48:10
    in C on your Linux machine,
    you don't want to have 50
  • 48:14 - 48:14
    copies of this LIB C library in
    memory.
  • 48:16 - 48:16
    You want to have just one.
    So what we want is that when
  • 48:20 - 48:20
    module B gets loaded,
    we want it to map this code
  • 48:23 - 48:23
    that's already been mapped into
    the address space of A into his
  • 48:28 - 48:28
    memory as well,
    OK?
  • 48:30 - 48:30
    And then, of course,
    we're going to have to load
  • 48:33 - 48:33
    additional memory for B itself,
    and for the library two which A
  • 48:36 - 48:36
    hasn't already loaded.
    So now, those are going to be
  • 48:39 - 48:39
    loaded into some additional
    locations in B's memory,
  • 48:42 - 48:42
    are going to be loaded into
    some additional locations in the
  • 48:45 - 48:45
    physical memory and mapped into
    A's memory.
  • 48:48 - 48:48
    So, this is just showing that
    we can actually use this notion
  • 48:51 - 48:51
    of address spaces,
    in addition to using it to
  • 48:54 - 48:54
    isolate two modules from each
    other so they can't refer to
  • 48:57 - 48:57
    each other's memory,
    we can also use it as a way to
  • 49:00 - 49:00
    allow two modules to share
    things with each other.
  • 49:04 - 49:04
    And in particular,
    this is a good idea in the case
  • 49:07 - 49:07
    of things like shared libraries
    where we have two things,
  • 49:09 - 49:09
    both programs need to be able
    to read the same data.
  • 49:12 - 49:12
    So they could use this to do
    that.
  • 49:14 - 49:14
    OK, so what we saw today was
    this notion of virtual memory
  • 49:17 - 49:17
    and address spaces.
    We saw how we have the kernel
  • 49:19 - 49:19
    that's a trusted intermediary
    between two applications.
  • 49:22 - 49:22
    What we're going to see next
    time is how we can take this
  • 49:25 - 49:25
    notion of virtualizing a
    computer.
  • 49:27 - 49:27
    We saw how to virtualize the
    memory today.
  • 49:30 - 49:30
    Next time we're going to see
    how we virtualize the processor
  • 49:33 - 49:33
    in order to create the
    abstraction of multiple
  • 49:35 - 49:35
    processors running on just one
    single, physical piece of
  • 49:38 - 49:38
    hardware.
    So I'll see you tomorrow.
Title:
Lec 6 | MIT 6.033 Computer System Engineering, Spring 2005
Description:

Virtualization and Virtual Memory

View the complete course at: http://ocw.mit.edu/6-033S05

License: Creative Commons BY-NC-SA
More information at http://ocw.mit.edu/terms
More courses at http://ocw.mit.edu

more » « less
Video Language:
English
Duration:
49:47

English subtitles

Revisions