CS3210 Design Operating Systems

Taesoo Kim





Rust 1: Thinking of Rust

Taesoo Kim

Administrivia

Q&A

  1. Is Rust suitable beyond OS development?

Will Rust be adopted by the biggest producers of systems software (Microsoft, Apple, Green Hills Software) and/or open-sourced systems software (Linux, FreeBSD)? What are some hurdles?

Q&A (continue)

  1. For linear types, why the function x.m() can’t be called twice?? → Lec 3
  2. Why is it so desirable to not have garbage collection? Does garbage collection impact OS/system development? → Lec3 (see, prep question!)
  3. MANY questions about unsafe → Lec 4

Summary: about operating system

Agenda

Motivating problem: file descriptor

Motivating problem: file descriptor

fd = open("/etc/passwd", O_RDONLY);
read(fd, buf, sizeof(buf));

// what if we forgot to close?
close(fd);

What about in Python?

fd = open("/etc/passwd")
fd.read()
with open("/etc/passwd") as fd:
  fd.read()

Implicit reclamation of file resources

fd = open("/etc/passwd")
fd.read()
# del fd -> fd.__del__() -> fd.close()
# fd.__enter__()
with open("/etc/passwd") as fd:
  fd.read()
# fd.__exit__()

Idea: using a scoping rule for returning resource

def func():
  fd = open("/etc/passwd") # <- open
  fd.read()
  # <- free
with open("/etc/passwd") as fd: # <- open
  fd.read()
# <- free

RAII: Resource Acquisition Is Initialization

void WriteToFile(const std::string& message) {
  static std::mutex mutex;
  std::lock_guard<std::mutex> lock(mutex);

  std::ofstream file("/etc/passwd");
  if (!file.is_open())
    throw std::runtime_error("unable to open file");
  file << message << std::endl;
}

Ref. Wikipedia: Resource acquisition is initialization

RAII: scoping rules and ownership

def func():
  fd = open("/etc/passwd") # <- open: fd owns the file
  fd.read()
  # <- free: reclaims the file

Ref. Ownership Based Resource Management (OBRM)

RAII: scoping rules and lifetime

  std::ofstream *file = new std::ofstream("/etc/passwd");
  ...
  // @different
  //    function
  //    thread
  //    long long time later
  //    or even when the process dies
  //    ...
  delete file;

Hello, Rust!

Rust: Practical Substructure Type System

→ Memory management can be done statically at compilation time!

Ref. Substructural Type Systems (aka, Linear, Affine type systems)

Agenda for this semester (tentative)!

Immutability and mutability

let x = 1;
x = 2;

Notes on error (E0384)

This error occurs when an attempt is made to reassign an 
immutable variable. For example:

  fn main() {
      let x = 3;
      x = 5; // error, reassignment of immutable variable
  }

By default, variables in Rust are immutable. To fix this error, 
add the keyword `mut` after the keyword `let` when declaring the 
variable. For example:

  fn main() {
      let mut x = 3;
      x = 5;
  }

Immutability and mutability

let mut x = 1;

dbg!(x); //=> x = 1

x = 2;

dbg!(x); //=> x = 2

Desugaring a bit

//
//      +--> variable name (or identifier)
//      |
let mut x: i32 = 1;
//  +--    ---   +-> value
//  |      |
//  |      +--> type (32-bit signed integer)
//  +--> noting mutability
//
x = 2;

Variable and type

Value and type

Strongly typed programming language

// x stores the value 1 (i32)
let mut x: i32 = 1;

// x is assigned with the value 2 (i32)
x = 2;

Assigning a value to a variable

Ownership

// x owns 1
let mut x: i32 = 1;

// x now owns 2
x = 2;

More interesting example

let mut x = String::from("Hello!");
let y = String::from("World!");

x = y; // typically say, assigning a value of y to x

dbg!(y);

Move semantics

let mut x = String::from("Hello!");
let y = String::from("World!");

// y's value is moved to x and x now owns "World!"
x = y;

// error: y doesn't own any value!
dbg!(y);

Move semantics and alloc()/free()

{
    // x <- alloc("Hello!")
    let mut x = String::from("Hello!");

    // y <- alloc("World!")
    let y = String::from("World!");

    // drop(x): free("Hello!")
    x = y;

    ...
} 
// drop(x): free("World!")

Behind the move operation

Borrowing via references

let x = String::from("Hello!");
let y = x; // move a value

dbg!(x); // WARNING. not allowed!
let x = String::from("Hello!");
let y = &x; // borrow a value (i.e., move a shared reference) 

dbg!(x);

Borrowing via references

  1. Rule 1. there can be many outstanding shared references
let x = String::from("Hello");
let y = &x; // borrow a value for reading
let z = &x; // borrow a value for reading

dbg!(y); //=> "Hello"
dbg!(z); //=> "Hello"
dbg!(x); //=> "Hello"

Borrowing via references

  1. Rule 2. there should be only one outstanding mutable reference
let mut x = String::from("Hello");
let y = &mut x; // borrow a value for writing
let z = &mut x; // borrow a value for writing
...

Borrowing via references

  1. Rule 3. there should be no shared and mutable reference at the same time
let mut x = String::from("Hello");
let y = &x;     // borrow a value for reading
let z = &mut x; // borrow a value for writing
...

Safety rule: {shared}+ xor mutable references

  1. Rule 1. there can be many outstanding shared references
  2. Rule 2. There should be only one mutable reference
  3. Safety rule: Rule 1 xor Rule 2

Implication 1. aliasing analysis

// $ man 3 memcpy
//
// NOTES
//   Failure to observe the requirement that the memory 
//   areas do not overlap has  been  the  source of
//   significant bugs.
//
void *memcpy(void *dst, const void *src, size_t n);
// dst and src can not point to the same value!
fn memcpy<T>(dst: &mut T, src: &T);

Implication 2. data race freedom

Big ideas in Rust

→ With modern type system, abstractions, build system, package managers, etc.

How other HLL tackle memory safety?

Important features for system languages

Notes on safety for OS development

→ We will try hard to distinguish what’s safe and unsafe in each labs

Agenda for this semester (tentative)!

Next lecture