Operating Systems 2021F Lecture 18

From Soma-notes
Revision as of 21:24, 18 November 2021 by Soma (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Video

Video from the lecture given on November 18, 2021 is now available:

Video is also available through Brightspace (Resources->Class zoom meetings->Cloud Recordings tab)

Notes

Lecture 18
----------
 - Restoring from access
 - ssh-agent
 - chmod
   - see man page on online explanations

 - supervisor vs user mode
 - what the kernel does
 - virtual memory
   - mmap
 - kernel modules
   - vs eBPF
 - filesystems
   - VFS
   - superblocks & inodes
 - character devices
   - creating, how they work

Restoring from access
 - look in .bash_aliases for original
 - run this to restore:
    rsync -a -v --delete --force
       <scs-username>@access.scs.carleton.ca:COMP3000VM-backup/
       /home/student/
 - note this will delete whatever you have in /home/student currently, in favor of the backup!
    - specify a new directory if you don't want this, like /home/student/Restore
 - only /home/student gets backed up
    - you can do other rsync commands to backup other things
    - but note those other files will probably be owned by other
      users
 - add a "-n" flag when you first run a new rsync command to  have it simulate what it would do otherwise


ssh-agent
 - this saves your unlocked private key locally, answers remote requests
   - can forward through multiple ssh connections
   - should really run locally
   - and it is running by default in most desktop environments
     (yes even windows and macOS)
 - so only need one ssh agent, all ssh connections you make will ask it for authentication info
    - if you are chaining ssh commands (ssh'ing to one machine, then from there to another) you may have to add the -A flag to forward the agent connection

 - note you can ssh to access from anywhere, so if you use -J with access you don't need a VPN to connect to the openstack instances



Supervisor vs user mode
 - CPU has at least two modes
    - user mode (ring 3 on x86)
    - supervisor mode (ring 0 on x86)
    - for ring 1 and 2, look up MULTICS
 - kernel runs in supervisor mode
 - processes run in user mode
 - system calls invoke the kernel by causing the CPU to switch
   to supervisor mode

Switching between CPU modes is expensive
 - because CPUs have lots of caches and registers,
   these have to be saved/restored/flushed on each switch

When in supervisor mode, you can do things that you can't do in user mode
 - let's discuss!

The kernel's job is to abstract and allocate resources to other programs
  - running in processes
  - so, implement the process abstraction

 - other hardware (than CPU & RAM) => character or block devices
 - block devices => files
 - one CPU => many CPUs 
    - share CPUs between processes
    - when a process runs, it gets a core to use
       - so it has to be kicked off periodically so other
         processes can use the same core
    - process scheduling: sharing CPUs
      (Lots to talk about scheduling, we'll do that later)
 - physical memory => virtual memory

Recall that when we looked at the address space of a process, memory locations were roughly the same for different programs
 - and could run same program with same addresses in parallel

Processes access memory using virtual addresses
 - a pointer contains a virtual address

Virtual memory is good because we don't have to care about where other processes put things in their own address space
 - different process, different set of addresses (different address space)

But at the end of the day we need to use RAM, and RAM is accessed by address
  - but these are physical addresses

So the kernel has to maintain a virtual->physical map for every process
 - this map is called a page table
 - every process has its own page table

On every memory access, the CPU has to take the virtual address and figure out the corresponding physical address
 - if you had to consult a big data structure every time, would be
   too slow
 - but CPU maintains a cache of virtual->physical mappings called the TLB
   (Translation Lookaside Buffer).  Don't ask what it means, just call
   it a TLB and blame some old IBM engineers

Why is this mapping called a page table?
 - because it maps pages to pages
 - a page is a fixed amount of RAM, power of 2
   - 4K or 8K, but can be multiple megabytes
   - different CPUs support different page sizes
 - basically equivalent to a block, but blocks are for disks
   and pages are for RAM

If you want to learn how CPUs and computers work at a low level and what makes them fast, learn computer architecture

Great book:
Computer Architecture: A Quantitative Approach 5th Edition
by John L. Hennessy (Author), David A. Patterson (Author)


virtual-physical memory mappings aren't done on an address by address basis
 - they are done on a page-by-page basis
   - so in 4K => 4K chunks
 - we divide virtual addresses into a page number and an offset
   - high bits are page number, lower bits are offset within page

So all virtual memory refers to is this maintaining of separate virtual address spaces per process and how these map to physical addresses (dynamically)

So why bother with all this machinery of virtual memory?
 - allows for easy sharing of RAM
    - can give a process pages from anywhere, don't
      need continuous ranges of memory
    - same memory used by multiple processes can map to same physical memory

So virtual memory is a great way to save on RAM usage
 - dynamic linking takes advantage of it, only need one copy of
   a library in memory and every process using it can share it
     (many virtual mappings but one range of physical RAM being used)
 
Remember all those calls to mmap?
 - tutorial 6, to access the file on disk
 - made by malloc back in tutorial 2
 - we see them whenever a dynamically linked process loads

mmap maps a file into the virtual address space
 - so logically, the file has been completely loaded into memory
 - but in reality, parts are loaded on demand

Different ranges of memory can have different permissions
 - read, write, execute
 - important for security, but also for performance
    - if memory is not writable and it came from disk,
      we don't have to worry about saving it if we need space,
      we can just kick it out of RAM

Think about fork
 - logically, we copy all of a process's memory
 - in practice, we just duplicate the virtual address mappings
   and mark everything read only
    - when one process writes, we duplicate that memory so the
      two processes have distinct copies


mmap can also be used to make "anonymous mmap's", so allocate RAM but without a corresponding file
 - so like sbrk, but can be anywhere

Can also mark memory regions as being private or shared
 - if private, then always make copies
 - if shared, then can be accessed by more than one thread/process

multithreaded processes is really just multiple process-like things (threads) that share virtual memory

If you see an mmap that is anonymous and uses a file descriptor of -1
 - it is allocating memory rather than loading a file

So fork/execve isn't as waseful as you'd think
 - fork, duplicate the process (but not much memory is actually being copied)
 - execve, throw out memory map and create a new one based on an
   executable on disk


Because of how much memory gets shared between processes, it is actually hard to say how much RAM a process is taking up

Many similarities & connections between files and process memory
  - just remember that files are normally persistent, and process memory is volatile

How does the Linux kernel make sure it doesn't allocate too much RAM?
 - it doesn't!  It just says yes and hopes things work out
   (it is "optimistic")

It has a few strategies to help out
 - program code loaded from disk can always be kicked out of RAM
 - data/modified pages in RAM can be written to a swap or page
   file/partition
 - but eventually we'll run out, and then the out of memory killer
   (OOM) gets to work
     - it will kill processes until there's enough physical memory
     - what it kills can be kind of random...and in general
       won't be what you want
 - because of the former mechanisms, normally the computer will slow
   to a crawl before actually needing to kill processes
     - because it will keep trying to free up physical memory,
       and eventually will spend most of its time doing that, rather
       than useful work

managing all of this is the Linux kernel's job.



Assume we have a process that uses 2G of virtual address space, 500M resident in RAM.
 - system has 8G of RAM total, 1G free

On such a system, that process can fork with no problem
 - won't end up using much more RAM because of sharing between
   parent and child

But what if the child now starts doing lots of work and uses that 2G of virtual address space, using everything
 - now we could easily use up that 1G of free RAM
   - and even get to the point where the kernel is looking for 500M

Linux is optimistic when allowing memory allocations because it allows full use of resources
 - if you want to put limits, you can impose quotas with cgroups

By the way, phones run the same basic kernels as desktops and servers nowadays
 - but they normally don't have swap
 - so, they have to be very aggressive in how they manage RAM
   - will proactively kill processes that aren't needed

Swap is where you "swap" a process to disk when we don't have enough
room for it in physical memory
 - used to be we'd have to move the entire process to disk,
   hence swapping
 - but now we can just move some of it (some pages, so moving to disk=>
   paging to disk)

We don't have swap on phones to
 - save power
 - save wear and tear on flash memory
    (flash can only be written to so much)

When you develop for a phone, your processes have to be ready to be
killed at any time
 - you tell the OS how to save necessary state so you can resume