Process Management

What is a Process

A program that is currently executing is called a process. Programs have 3 sections: text (code), data and stack. Data grows up, and stack down, as required.

process%20data.png

Processes also have 3 components; the text (code), required data (data + stack) and information needed to manage the program (e.g. registers, stack pointer).

Processes own resources allocated to them, and might encompass multiple threads. Each Process has;

  • Address Space
  • Global variables (concurrency control between threads or we get race conditions!)
  • Open Files
  • Statistics/accounting
  • Signals/signal handling (like interrupts like keypads)
  • Child process

Multiprocessor Systems

Either Uniform Memory Access or Non-Uniform Memory Access. We will look at UMA:

Bus-Based

Having 2+ processors connected via bus is the simplest way of doing it. But with many processors the bus bandwidth becomes a limiting factor. Each processor has a cache though, so hopefully they need to access their cache rather than memory via bus.

Of course caches make consistency hard, so usually hardware propagates changes to the cache to the other caches (also consumes bus bandwidth).

We can use a crossbar switchbased system (instead of 1 bus) but number of switches = num cpu * num cpu (huge!).

OS-details

To guarantee synchronisation we have to have hardware support.

Test and set:
Hardware locks busses during Test-and set instruction stuff which leads to all kindsa issues. So we test before hand (using our cached variable) and then only test-and-set once our test comes back positive.

Queue lock: So we just give each processor a spinlock and when one releases it, it unlocks the lock below, allowing it to move on. This queuelock thing scales super well but has a high overhead. Spinlocks are the opposite, test and set or test and test and set are both the bads.

Per-processor OS

Used at first. Easy to implement, just statically allocate each CPU its own memory and then share peripherals. Shared file consistency is hard, and we can't really take advantage of sharing the load across the processors.

Master-Slave OS

One OS handles all the OS operations and system calls. User level applications run on the other CPUs, and master CPU if there's time. Memory can be allocated as needed to be efficient, it's relatively simple to implement and doesn't have any extra consistency issues. BUT Master CPU becomes the bottleneck.

Symmetric Multiprocessors

Kernel in every processor. Hella concurrency issue. We can mutex the whole kernel, which makes it tinily better than master-slave OS, but then the lock becomes a bottleneck.

Split-Kernel

Find mostly independent sections of the kernel and put them on different processors. It's really hard to do this! But does allow parallelism. We can mutex each part, but then we might end up with deadlocks! Oh noes. Gets quite complicated.

Spinning vs Switching

Switching processes takes time (context switch save state etc). But spinning is just a direct waste of CPU time, so both are bad. It's a tradeoff - if the lock will be held for less than switching time, fucking spin.

Spinlock implementations disable interrupts in addition to acquiring locks to avoid lock-holder preemption, cause once a holder gets a lock, they get it until they're done, even if they're pre-empted. Hence we stop pre-emption happening.

Scheduling

We can just have a single ready queue and take the first thing off it when a processor goes idle. BUT:

  • Locks are bottleneck-y
  • We want to run processes on the same processors, so the cache has more relevant stuff

So we can have two scheduling things; one that runs per processor and does the standard scheduling stuff (round robin queue, etc). And one that runs overall and decides which processes belong to which processors, balancing the load. Then if a processor queue has nothing ready it gives it something.