(Press ?
for help, n
and p
for next and previous slide; usage hints)
Mo, Yilin
Sept 2020
void main() { // set up serial console uart_init(); // say hello uart_puts("Hello World!\n"); // echo everything back while(1) { uart_send(uart_getc()); } }
Source code from Github
void uart_init() { register unsigned int r; /* initialize UART */ *AUX_ENABLE |=1; // enable UART1, AUX mini uart *AUX_MU_CNTL = 0; *AUX_MU_LCR = 3; // 8 bits *AUX_MU_MCR = 0; *AUX_MU_IER = 0; *AUX_MU_IIR = 0xc6; // disable interrupts *AUX_MU_BAUD = 270; // 115200 baud /* map UART1 to GPIO pins */ r=*GPFSEL1; r&=~((7<<12)|(7<<15)); // gpio14, gpio15 r|=(2<<12)|(2<<15); // alt5 *GPFSEL1 = r; *GPPUD = 0; // enable pins 14 and 15 r=150; while(r--) { asm volatile("nop"); } *GPPUDCLK0 = (1<<14)|(1<<15); r=150; while(r--) { asm volatile("nop"); } *GPPUDCLK0 = 0; // flush GPIO setup *AUX_MU_CNTL = 3; // enable Tx, Rx }
void uart_send(unsigned int c) { /* wait until we can send */ do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x20)); /* write the character to the buffer */ *AUX_MU_IO=c; }
void uart_puts(char *s) { while(*s) { /* convert newline to carrige return + newline */ if(*s=='\n') uart_send('\r'); uart_send(*s++); } }
char uart_getc() { char r; /* wait until something is in the buffer */ do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01)); /* read it and return */ r=(char)(*AUX_MU_IO); /* convert carrige return to newline */ return r=='\r'?'\n':r; }
“Figure 1.1 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
“Figure 1.1 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
Monolith-, Micro- and a "hybrid" kernel
under CC0 1.0; from Wikimedia Commons
See this map of the Linux kernel for a real-life monolithic example
address
selects a byte (not a word as in Hack)Web browser may open multiple processes
BoundedBuffer
located at RAM address 42
Memory Layout for a C program
#include <stdio.h> #include <malloc.h> const char *hello = "Hello world!\n"; int b; int main() { int a = 1; b = 2; void *p = malloc(1000); printf(hello); return 0; }
smem
demo later on
“Figure 6.4 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
smem
smaps
files: smem
“Figure 6.9 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
When process opens file, file can be mapped into virtual address space
Sample allocation of frames to some process
“Figure 6.10 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
Consider previously shown RAM allocation (Fig. 6.10)
“Figure 6.10 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
Page table for that situation (Fig. 6.11)
Valid | Frame# |
---|---|
1 | 1 |
1 | 0 |
0 | X |
0 | X |
0 | X |
0 | X |
1 | 3 |
0 | X |
“Figure 6.4 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
TinyOS ([TinyOS])
The language(NeS) does NOT support dynamic memory allocation; components statically declare all of a program’s state, which prevents memory fragmentation as well as runtime allocation failures. The restriction sounds more onerous than it is in practice; the component abstraction eliminates many of the needs for dynamic allocation. In the few rare instances that it is truly needed (e.g., TinyDB ), a memory pool component can be shared by a set of cooperating components.
Threads!
© 2016 Julia Evans, all rights reserved; from julia's drawings. Displayed here with personal permission.
Concurrency is more general term than parallelism
Parallel threads (on multiple CPU cores)
Interleaved threads (taking turns on single CPU core)
Figure 3.3 of [Hai17]
by Max Hailperin under CC BY-SA 3.0; converted from GitHub
yield
In the following
switchFromTo()
on next slide
yield()
may
really be an OS system call
Interleaved execution of threads. Based on Figure 2.7 of book by Max Hailperin, CC BY-SA 3.0.
by Jens Lechtenbörger under CC BY-SA 4.0; from GitLab
switchFromTo()
is cooperative
Other
Note: Above goals have trade-offs (discussed subsequently)
Key ingredients
“Ferrari Kiss” by Antoine Valentini under CC BY-SA 2.0; from flickr
Time | Thread 1 | Thread 2 | Variables |
1 | c = x | \(x = 0, t1.c = 0, t2.c = 0\) | |
2 | c += 1 | \(x = 0, t1.c = 1, t2.c = 0\) | |
3 | c = x | \(x = 0, t1.c = 1, t2.c = 0\) | |
4 | x = c | \(x = 1, t1.c = 1, t2.c = 0\) | |
5 | c += 1 | \(x = 1, t1.c = 1, t2.c = 1\) | |
6 | x = c | \(x = 1, t1.c = 1, t2.c = 1\) |
What if we use the following increment_global
function:
def increment_global(): global x x += 1
An atomic action is one that appears to take place as a single indivisible operation:
x += 1
are not atomic
lock()
or acquire()
: Lock/acquire/take the object
lock()
’ed by one thread at a timelock()
need to wait for unlock()
unlock()
or release()
: Unlock/release the object
lock()
’ed again afterwards
“Figure 4.4 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
Dining Philosophers
“Figure 4.20 of [Hai17]” by Max Hailperin under CC BY-SA 3.0; converted from GitHub
SurgeC
module BlinkTaskC { uses interface Timer<TMilli> as Timer0; uses interface Leds; uses interface Boot; } implementation { task void toggle() { call Leds.led0Toggle(); } event void Boot.booted() { call Timer0.startPeriodic( 1000 ); } event void Timer0.fired() { post toggle(); } }
Two-Level Scheduling
FreeRTOS Architecture
Support:
FreeRTOS Scheduler
Bare Metal | TinyOS | FreeRTOS | Linux | |
Overhead | None | Small | Small | Large |
Scheduler | Single Thread | FIFO + Interrupt | Round-Robin, Real-time | CFS, NOT real-time |
Synchronization | None | Resolved at Compilation | Mutex, Semaphores | Multiple Locking Types |
Memory | Static | Static | Static or 5 Dynamic schemes | Full fledged Memory Management |