Introduction to Operating Systems

Overview

What happens when a computer program run?
  • The fundamental Von Neumann model of computing.
  • The process
    • fetches an instruction from memory,
    • decodes the instruction, and
    • executes the instruction.
Why do we need OS?
  • What a programmer see is all code, lines of codes.
  • Underneath, there is a complex ecosystem of hardware components.
  • How do we hide this complexity away from the programmers?
How do the OS help?
  • Each physical component in a computing system is considered a resource.
  • The OS manages these resources so that multiple programs can access these resources (through the corresponding virtual interface) at the same time.
  • This is called concurrency.

Which of the following best describes the primary role of an Operating System?

Virtualization

Preparation
  • The source code examples from the OSTEP book is located inside the container at /home/student/ostep-code
  • For this lecture, we will build the source codes inside the intro subdirectory.
  • Launch the csc331 container if necessary, then open a bash terminal into the container.
    • In docker-compose.yml, the following setting is made cpusets: "0-1". This means the container is limited to run on the physical cores 0 and 1 of the CPU, which is to emulate a simple dual-core configuration.
1
cpuset: "0-1"
  • View the file list
  • Compile all the files using make
1
2
3
4
5
docker compose up -d
docker compose exec -it csc331 /bin/bash
cd ~/ostep-code/intro
ls
make
CPU Virtualization

The example program, cpu.c, will run an infinite loop that prints out the first command line argument:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>
#include "common.h"

int main(int argc, char *argv[])
{
    if (argc != 2) {
	fprintf(stderr, "usage: cpu <string>\n");
	exit(1);
    }
    char *str = argv[1];

    while (1) {
	printf("%s\n", str);
	Spin(1);
    }
    return 0;
}
  • Open two terminals, and connect to the running container from these two terminals

  • In the left terminal pane, run the following command.

1
./cpu A & ./cpu B & ./cpu C &./cpu D 
  • To stop the running processes on the left pane, move to the right pane and running the following commands:
1
ps aux | grep cpu
  • Identify the process ID (the second columns), then use the kill to kill all the process IDs (see figure below).
  • The illusion of infinite CPUs
    • A limited number of physical CPUs can still be represented as infinite number of CPUs through virtualization.
    • The OS will manage the scheduling and allocation of the actual run on physical resources.
Memory Virtualization
  • In the left terminal pane, run the following commands:
    • -R will disable randomization of virtual memory address space for shells.
1
2
3
clear
sudo setarch `uname -m` -R /bin/bash
./mem 100 &./mem 200
  • In the right pane, use the same procedure as above to kill the two running programs after a few iterations.

Do programs running concurrently occupy the same memory locations (addresses)?

No

The illusion of dedicated memory resources

  • Many running program share the physical memory space.
  • Each runnning program is presented with the illusion that they have access to their own private memory. This is called virtual address space, which is mapped to physical memory space by the OS.
  • Making memory references within one running program (within one’s own virtual address space) does not affect the private virtual address space of others.
  • Without the setarch command, the location of variable p will be randomize within the virtual address space of a process. This is a security mechanism to prevent others from guessing and applying direct manipulation techniques to the physical memory location that acually contains p.

Concurrency

1
2
3
./threads 50
./threads 100
./threads 200
1
2
3
4
./threads 20000
./threads 30000
./threads 30000
./threads 30000
Problem with concurrency
  • Naive concurrency gives you wrong results.
  • Naive concurrency gives you wrong and inconsistent results.
Why does this happen?
  • At machine level, incrementing counter involves three steps:
    • Load value of counter from memory into register,
    • Increment this value in the register, and
    • Write the value of counter back to memory.
  • What should have happened:
    • One thread increments counter (all three steps), then the other thread increments counter, now with the updated value.
  • What really happened:
    • One thread increments counter.
    • While this thread has not done with all three steps, the other thread steps in and attempts to increment the stale content of counter in memory.

Persistency

A Brief History

A good paper to read: Hanser, Per Brinch. “The evolution of operating systems” 2001