Compare commits
10
Commits
3dc29f36e5
...
12af283ec7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12af283ec7
|
||
|
|
31d9e78f3c
|
||
|
|
cedd1d98ec
|
||
|
|
06a0189e2e
|
||
|
|
9c947ed91e
|
||
|
|
695a79e106
|
||
|
|
724b6988db
|
||
|
|
b4196dcfaa
|
||
|
|
dcacadffc0
|
||
|
|
42c2105b16
|
+60
@@ -0,0 +1,60 @@
|
||||
* Talks
|
||||
|
||||
This repository contains slides and example code from two technical presentations given in 2020.
|
||||
|
||||
Slides use [[https://orgmode.org/][org-mode,]] and were presented in Emacs with [[https://github.com/rlister/org-present][org-present]].
|
||||
|
||||
** [[file:concurrency/][Concurrency]] (June 2020)
|
||||
|
||||
Introduction to concurrency.
|
||||
|
||||
** Slides
|
||||
- Sequential vs. concurrent execution, I/O latency, interrupts, and cooperative vs. preemptive scheduling
|
||||
- Concurrency vs. parallelism, processes vs. threads, and language runtimes (C, JavaScript event loop, Go scheduler)
|
||||
- Shared memory synchronization vs. message passing, backpressure, and distributed data races
|
||||
|
||||
** Examples
|
||||
- temperatures: concurrent HTTP requests fetching city temperatures from OpenWeatherMap.
|
||||
- mutex: data race demonstration and mutual exclusion on a shared bank account balance (~go run -race ./main.go~).
|
||||
- csp: pipeline of goroutines and channels reading a CSV of zip codes, querying weather data with a concurrency limiter, and displaying a live top-ten leaderboard.
|
||||
|
||||
*** Running Go examples
|
||||
|
||||
#+begin_src shell
|
||||
# Mutex example (with and without race detector):
|
||||
cd concurrency/cmd/mutex
|
||||
go run ./main.go
|
||||
go run -race ./main.go
|
||||
|
||||
# Weather examples (require an OpenWeatherMap API key):
|
||||
export OPEN_WEATHER_MAP_API_KEY="..."
|
||||
cd concurrency/cmd/temperatures && go run .
|
||||
cd concurrency/cmd/csp && go run .
|
||||
#+end_src
|
||||
|
||||
** [[file:x86-assembly/][x86 Assembly]] (November 2020)
|
||||
|
||||
A brief tour of x86-64 assembly on Linux.
|
||||
|
||||
** Slides
|
||||
- Compilation pipeline, ISAs, CPU registers, and Linux system calls
|
||||
- Instructions, addressing modes, control flow, and memory safety
|
||||
- Virtual memory, the stack, function prologues/epilogues, and the System V AMD64 ABI calling convention
|
||||
|
||||
** Examples
|
||||
- exit: minimal program invoking the ~exit~ system call.
|
||||
- math: basic arithmetic instructions (~mov~, ~add~, ~sub~, ~mul~, ~div~), inspected via exit status.
|
||||
- control-flow: loops, comparisons (~cmp~), and conditional/unconditional jumps (~je~, ~jmp~).
|
||||
- hello-world: writing a string from the ~.data~ section to stdout with the ~write~ system call.
|
||||
- safety: out-of-bounds memory overwrite across adjacent ~.data~ buffers.
|
||||
- stack: pushing bytes onto the stack and printing from ~%rsp~.
|
||||
- func: function call, stack frame setup (~%rbp~ / ~%rsp~), and return.
|
||||
|
||||
*** Running x86-64 examples
|
||||
|
||||
#+begin_src shell
|
||||
as exit.s -o exit.o
|
||||
ld exit.o -o exit
|
||||
./exit
|
||||
echo $?
|
||||
#+end_src
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/dwrz/talks/concurrency/pkg/weather"
|
||||
"code.chimeric.al/dwrz/talks/concurrency/pkg/weather"
|
||||
)
|
||||
|
||||
func getZipTemp(
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/dwrz/talks/concurrency/pkg/weather"
|
||||
"code.chimeric.al/dwrz/talks/concurrency/pkg/weather"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
module github.com/dwrz/talks/concurrency
|
||||
module code.chimeric.al/dwrz/talks/concurrency
|
||||
|
||||
go 1.14
|
||||
go 1.27.3
|
||||
|
||||
+96
-83
@@ -4,7 +4,7 @@
|
||||
- Senior Software Engineer @ Good Uncle
|
||||
- dwrz@dwrz.net
|
||||
|
||||
Slides and code: https://github.com/dwrz/talks/concurrency.
|
||||
Slides and code: https://code.chimeric.al/dwrz/talks.
|
||||
|
||||
* 2. Why
|
||||
|
||||
@@ -61,7 +61,7 @@ A second is a duration, not an instant.
|
||||
|
||||
Two things can happen in the same second and not be simultaneous.
|
||||
|
||||
In computer science, this nuance matters.
|
||||
With computers, this nuance matters.
|
||||
|
||||
* 9. Time
|
||||
|
||||
@@ -98,10 +98,12 @@ Source: [[https://www.prowesscorp.com/computer-latency-at-a-human-scale/]]
|
||||
* 12. Cooking Recipes
|
||||
|
||||
- Step-by-step
|
||||
- Concurrent
|
||||
- Pre-heat oven
|
||||
- Boil water
|
||||
- Timers
|
||||
- Concurrent:
|
||||
- Handle other tasks while:
|
||||
- Preheating oven
|
||||
- Boiling water
|
||||
- Waiting for timers
|
||||
- Switching back to a task when it is ready for more work.
|
||||
|
||||
* 13. Multitasking
|
||||
|
||||
@@ -117,24 +119,28 @@ Computers can "simultaneously" work on:
|
||||
Combination of:
|
||||
- Operating System *Scheduler*
|
||||
- Hardware (/interrupts/)
|
||||
- Keyboard
|
||||
- Filesystem
|
||||
- Network Card
|
||||
- Timer
|
||||
|
||||
* 17. Cooperative Scheduling
|
||||
* 16. Cooperative Scheduling
|
||||
[[file:images/cooperative-scheduling.jpg]]
|
||||
|
||||
* 18. Preemptive Scheduling
|
||||
* 17. Preemptive Scheduling
|
||||
[[file:images/preemptive-scheduling.jpg]]
|
||||
|
||||
* 19. Preemptive Scheduling
|
||||
* 18. Preemptive Scheduling
|
||||
[[file:images/preemptive-scheduling-tree.jpg]]
|
||||
|
||||
* 20. Multi Core
|
||||
* 19. Multi Core
|
||||
[[file:images/preemptive-scheduling-multi-core.jpg]]
|
||||
|
||||
* 21. Concurrency versus Parallelism
|
||||
* 20. Concurrency versus Parallelism
|
||||
|
||||
- Parallelism :: performing computations at the same time.
|
||||
- Multiple cores.
|
||||
- Multiples computers.
|
||||
- Multiple computers.
|
||||
- Kind of like having multiple cooks in a kitchen.
|
||||
- Can do different things at the same time.
|
||||
- Can do one job faster (peeling a lot of potatoes).
|
||||
@@ -142,13 +148,14 @@ Combination of:
|
||||
- Concurrency :: performing computations over inter-leaving, non-sequential time periods.
|
||||
- Single or multi-core.
|
||||
|
||||
* 22. Processes and Threads
|
||||
* 21. Processes and Threads
|
||||
A little more nuance.
|
||||
|
||||
- Different /recipes/
|
||||
- Different /parts/ of a recipe
|
||||
|
||||
- Process :: a program in execution.
|
||||
- Unique resources (memory, code, files, etc.).
|
||||
- Start off with one thread.
|
||||
- Can /fork/.
|
||||
- Communicate via inter-process communication (IPC).
|
||||
@@ -157,17 +164,17 @@ A little more nuance.
|
||||
- Share resources ∴ less resource intensive.
|
||||
- Can communicate via shared resources.
|
||||
|
||||
* 23. Processes and Threads
|
||||
* 22. Processes and Threads
|
||||
|
||||
[[file:images/process-vs-thread.jpg]]
|
||||
|
||||
Source: https://stackoverflow.com/questions/16354460/forking-vs-threading/16354658
|
||||
|
||||
* 24. htop
|
||||
* 23. htop
|
||||
|
||||
Press ~H~ to show threads.
|
||||
|
||||
* 25. Languages and Runtimes
|
||||
* 24. Languages and Runtimes
|
||||
|
||||
- ~C~, ~C++~, ~Rust~ allow for fine-grained control of processes and threads.
|
||||
#+begin_src C :output raw
|
||||
@@ -180,7 +187,7 @@ int main() {
|
||||
}
|
||||
#+end_src
|
||||
|
||||
* 26. JavaScript
|
||||
* 25. JavaScript
|
||||
- Single-threaded (for application code).
|
||||
- Runtimes implement concurrency via an event loop.
|
||||
|
||||
@@ -188,42 +195,7 @@ int main() {
|
||||
|
||||
Source: [[https://medium.com/preezma/node-js-event-loop-architecture-go-deeper-node-core-c96b4cec7aa4]]
|
||||
|
||||
* 27. Go
|
||||
Runtime multi-threading, multiplexes goroutines onto threads.
|
||||
|
||||
[[file:images/go-scheduler.png]]
|
||||
|
||||
Source: https://freethreads.net/2019/01/23/go-runtime-scheduler-design-internals/
|
||||
|
||||
* 28. Quick Review
|
||||
|
||||
- Sequential versus Concurrent Steps
|
||||
- Compare with recipes.
|
||||
- Concurrency versus Parallelism
|
||||
- Parallelism: multiple cooks, simultaneously /doing/.
|
||||
- Concurrency: one or more cooks, simultaneously /dealing/ with many tasks.
|
||||
- Operating System Scheduler
|
||||
- Processes and Threads
|
||||
- Languages and Runtimes
|
||||
|
||||
* 29. Part 2: Using Concurrency
|
||||
|
||||
Typical use cases:
|
||||
- Network I/O
|
||||
- Requests between clients and servers.
|
||||
- Requests to a database.
|
||||
- Requests between microservices.
|
||||
- Requests to external APIs.
|
||||
|
||||
- User interfaces
|
||||
- Capturing user input
|
||||
- Rendering output
|
||||
|
||||
- Similar, but independent computations.
|
||||
|
||||
- Solving problems that are best expressed with concurrency.
|
||||
|
||||
* 30. JavaScript
|
||||
* 26. Aside: Event Loop
|
||||
|
||||
What is the output of the following? Why?
|
||||
#+begin_src javascript
|
||||
@@ -238,14 +210,49 @@ setTimeout(() => console.log('2'), 0);
|
||||
console.log('end');
|
||||
#+end_src
|
||||
|
||||
* 31. Go Examples
|
||||
* 27. Go
|
||||
*Runtime scheduler* manages multiple threads.
|
||||
[[file:images/go-scheduler.png]]
|
||||
|
||||
Source: https://freethreads.net/2019/01/23/go-runtime-scheduler-design-internals/
|
||||
|
||||
* 28. Quick Review
|
||||
|
||||
- Sequential versus Concurrent Steps
|
||||
- Compare with recipes.
|
||||
- Concurrency versus Parallelism
|
||||
- Parallelism: multiple cooks, simultaneously /doing/.
|
||||
- Concurrency: one or more cooks, /dealing/ with many tasks.
|
||||
- Operating System Scheduler
|
||||
- Processes and Threads
|
||||
- Languages and Runtimes
|
||||
|
||||
* 29. Part 2: Using Concurrency
|
||||
|
||||
Typical use cases:
|
||||
- Network I/O between:
|
||||
- Clients and servers.
|
||||
- Services and database(s).
|
||||
- Microservices.
|
||||
- Service and external APIs.
|
||||
|
||||
- User interfaces
|
||||
- Capturing user input
|
||||
- Rendering output
|
||||
|
||||
- Similar, but independent computations.
|
||||
|
||||
- Solving problems that are best expressed with concurrency.
|
||||
|
||||
* 30. Go Examples
|
||||
|
||||
1. Shared Memory with Mutex (Mutual Exclusion)
|
||||
- Pitfall: synchronization, debugging.
|
||||
2. Message Passing (Channels and Goroutines)
|
||||
- Pitfall: resources usage, complexity.
|
||||
|
||||
* 32. Mutex Example
|
||||
2. Message Passing (Channels and Goroutines)
|
||||
- Pitfall: resource usage, complexity.
|
||||
|
||||
* 31. Mutex Example
|
||||
Concurrent updates to a bank account balance.
|
||||
|
||||
Code:
|
||||
@@ -253,31 +260,37 @@ Code:
|
||||
|
||||
Run: [[file:cmd/mutex/run.sh]]
|
||||
- Without mutex.
|
||||
- With mutex.
|
||||
|
||||
* 33. Data Races
|
||||
* 32. Data Races
|
||||
[[file:images/data-race.jpg]]
|
||||
|
||||
* 34. Mutex
|
||||
* 33. Mutex
|
||||
|
||||
/Mutual Exclusion/
|
||||
|
||||
- Purpose: protect a critical section of code.
|
||||
- Effect: serialization.
|
||||
|
||||
* 35. Race Detector
|
||||
Run: [[file:cmd/mutex/run.sh]]
|
||||
- With mutex.
|
||||
|
||||
* 34. Race Detector
|
||||
|
||||
How can we be sure?
|
||||
|
||||
Race detector can give us /some/ confidence (but no guarantees).
|
||||
|
||||
- Without mutex.
|
||||
- With mutex.
|
||||
|
||||
* 36. Tradeoffs: Validating Correctness, Testing, Debugging
|
||||
* 35. Tradeoffs: Validating Correctness, Testing, Debugging
|
||||
|
||||
- Non-deterministic behavior
|
||||
- Works fine on one run, fatal bug in another.
|
||||
- Up to how the scheduler(s) order things at execution time.
|
||||
- Concealed bugs
|
||||
|
||||
* 37. Message Passing
|
||||
* 36. Message Passing
|
||||
|
||||
Proverbs:
|
||||
#+begin_quote
|
||||
@@ -286,7 +299,7 @@ Don't communicate by sharing memory, share memory by communicating.
|
||||
Channels orchestrate, mutexes serialize.
|
||||
#+end_quote
|
||||
|
||||
* 38. Requirements
|
||||
* 37. Program Requirements
|
||||
|
||||
- Read CSV of zip codes
|
||||
- 50 in our example
|
||||
@@ -298,27 +311,27 @@ Channels orchestrate, mutexes serialize.
|
||||
|
||||
- Display running list of top ten temperatures.
|
||||
|
||||
* 39. Concurrency Hallmarks
|
||||
* 38. Concurrency Hallmarks
|
||||
|
||||
- Repeatedly processing similar data (lines of CSV)
|
||||
- Network I/O
|
||||
- UI I/O
|
||||
|
||||
* 40. Message Passing Example
|
||||
* 39. Message Passing Example
|
||||
|
||||
Run: [[file:cmd/csp/run.sh]]
|
||||
|
||||
* 41. Code Walkthrough
|
||||
* 40. Code Walkthrough
|
||||
|
||||
[[file:cmd/csp/main.go]]
|
||||
|
||||
* 42. Channels and Goroutines
|
||||
* 41. Channels and Goroutines
|
||||
[[file:images/message-passing-example-diagram.jpg]]
|
||||
|
||||
* 43. Channels and Goroutines
|
||||
* 42. Channels and Goroutines
|
||||
[[file:images/go-scheduler.jpg]]
|
||||
|
||||
* 44. Scaling
|
||||
* 43. Scaling
|
||||
|
||||
What if:
|
||||
- We wanted to process the entire list of 42,000 zip codes?
|
||||
@@ -326,7 +339,7 @@ What if:
|
||||
|
||||
Could we just get rid of the limiter?
|
||||
|
||||
* 45. Resources
|
||||
* 44. Resources
|
||||
|
||||
Probably not, because:
|
||||
|
||||
@@ -339,12 +352,12 @@ Probably not, because:
|
||||
- Better data structures and algorithms could help here.
|
||||
- Goroutines are cheap, but not free.
|
||||
|
||||
* 46. Possible Solutions
|
||||
* 45. Possible Solutions
|
||||
|
||||
- Use a semaphore to limit concurrency.
|
||||
- Buffers
|
||||
|
||||
* 47. Designing and Managing Concurrency
|
||||
* 46. Designing and Managing Concurrency
|
||||
|
||||
- Size of input matters.
|
||||
- Resources
|
||||
@@ -353,7 +366,7 @@ Probably not, because:
|
||||
- Backpressure
|
||||
- Complexity
|
||||
|
||||
* 48. Quick Review
|
||||
* 47. Quick Review
|
||||
|
||||
Techniques for implementing and managing concurrency:
|
||||
- Event loop
|
||||
@@ -367,7 +380,7 @@ Concurrency Pitfalls:
|
||||
- Complexity
|
||||
- Need to think differently about concurrent code.
|
||||
|
||||
* 49. Part 3: Infrastructure Concurrency
|
||||
* 48. Part 3: Infrastructure Concurrency
|
||||
|
||||
Concurrent applications running concurrently on distributed computers.
|
||||
|
||||
@@ -376,20 +389,20 @@ Concurrent applications running concurrently on distributed computers.
|
||||
- Databases
|
||||
- Host and Network Infrastructure
|
||||
|
||||
* 50. Example
|
||||
* 49. Example
|
||||
[[file:images/infrastructure-data-race.jpg]]
|
||||
|
||||
* 51. Review
|
||||
* 50. Review
|
||||
|
||||
- What concurrency is
|
||||
- Sequential versus concurrent
|
||||
- Comparison with recipes
|
||||
- Comparison with recipes and cooks
|
||||
- Compared to parallelism
|
||||
- How it is implemented
|
||||
- Operating System Scheduler and Interrupts
|
||||
- Languages and Runtimes
|
||||
|
||||
* 52. Review
|
||||
* 51. Review
|
||||
|
||||
- When, why, and how to use it
|
||||
- I/O + UI
|
||||
@@ -398,16 +411,16 @@ Concurrent applications running concurrently on distributed computers.
|
||||
|
||||
- Pitfalls
|
||||
- Synchronization
|
||||
- Resources Usage
|
||||
- Resource Usage
|
||||
- Debugging
|
||||
- Complexity
|
||||
|
||||
* 53. Review
|
||||
* 52. Review
|
||||
|
||||
- Infrastructure
|
||||
- Data Race
|
||||
|
||||
* 54. Conclusion
|
||||
* 53. Conclusion
|
||||
|
||||
- Goals:
|
||||
- Give you a rough sense of what concurrency is.
|
||||
@@ -421,7 +434,7 @@ Concurrent applications running concurrently on distributed computers.
|
||||
- Tradeoffs and economics (resources, costs, developer time).
|
||||
- Learning is continuous...
|
||||
|
||||
* 55. Further Reading
|
||||
* 54. Further Reading
|
||||
|
||||
- [[https://slikts.github.io/concurrency-glossary/][Concurrency Glossary]]
|
||||
- [[https://blog.golang.org/waza-talk][Concurrency is not parallelism]]
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
* 1. x86 Assembly
|
||||
|
||||
- David Wen Riccardi-Zhu
|
||||
- Senior Software Engineer @ Good Uncle
|
||||
- dwrz@dwrz.net
|
||||
|
||||
Slides and code: https://code.chimeric.al/dwrz/talks.
|
||||
|
||||
* 2. Overview
|
||||
|
||||
1. Follow a trail of questions into the depths of the machine.
|
||||
2. Peel away some abstractions on the way.
|
||||
3. Gloss over a bunch of stuff (we have an hour, not a semester).
|
||||
4. Surface (hopefully) with a better understanding of how computers work.
|
||||
|
||||
* 3. What does this program do?
|
||||
|
||||
#+begin_src bash :results raw
|
||||
node -e "console.log('Hello, World');"
|
||||
#+end_src
|
||||
|
||||
* 4. How does this program /work/?
|
||||
|
||||
#+begin_src bash :results raw
|
||||
node -e "console.log('Hello, World');"
|
||||
#+end_src
|
||||
|
||||
How does "Hello World" get onto the terminal?
|
||||
|
||||
* 5. What does this program do?
|
||||
Let's try something simpler.
|
||||
|
||||
#+begin_src bash
|
||||
node -e ""
|
||||
#+end_src
|
||||
|
||||
Does this do /anything/?
|
||||
|
||||
* 6. It does /something/.
|
||||
|
||||
We can tell, because it takes a few milliseconds to execute.
|
||||
|
||||
#+begin_src bash :results raw
|
||||
time node -e ""
|
||||
#+end_src
|
||||
|
||||
#+begin_src bash :results raw
|
||||
time node -e "console.log('Hello, World');"
|
||||
#+end_src
|
||||
|
||||
* 7. It /definitely/ does /something/.
|
||||
|
||||
We can tell, because it's making system calls.
|
||||
|
||||
#+begin_src bash :results raw
|
||||
strace node -e ""
|
||||
#+end_src
|
||||
|
||||
Lot of noise for a program that does "nothing"...
|
||||
|
||||
#+begin_src bash :results raw
|
||||
strace node -e "console.log('Hello, World');"
|
||||
#+end_src
|
||||
|
||||
* 8. JavaScript
|
||||
We've actually started with something difficult.
|
||||
|
||||
JavaScript is an interpreted language.
|
||||
|
||||
#+begin_src js
|
||||
const myProgram = '0 + 1'.replace('+', '-');
|
||||
|
||||
const result = eval(myProgram);
|
||||
|
||||
console.log(result); // -1
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
|
||||
* 9. C
|
||||
Let's use a simpler example.
|
||||
|
||||
C is compiled.
|
||||
|
||||
We create our own program, rather than use a program that interprets strings.
|
||||
|
||||
#+begin_src C :results raw
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
printf("Hello, World\n");
|
||||
}
|
||||
#+end_src
|
||||
|
||||
* 10. Compile and Execute
|
||||
|
||||
#+begin_src bash
|
||||
gcc hello-world.c -o hello-world
|
||||
#+end_src
|
||||
|
||||
#+begin_src bash
|
||||
./hello-world
|
||||
#+end_src
|
||||
|
||||
#+begin_src bash
|
||||
strace ./hello-world
|
||||
#+end_src
|
||||
|
||||
* 11. write
|
||||
This seems familiar:
|
||||
#+begin_src C
|
||||
write(1, "Hello, World\n", 13Hello, World
|
||||
) = 13
|
||||
#+end_src
|
||||
|
||||
It's in our node program, too:
|
||||
#+begin_src bash
|
||||
strace node -e "console.log('Hello, World');" 2>&1 | grep "Hello"
|
||||
#+end_src
|
||||
|
||||
/What is this?/
|
||||
What is ~write~?
|
||||
What is 1?
|
||||
What is 13?
|
||||
|
||||
* 12. System Calls
|
||||
~strace~ shows us system calls. ~write~ is a system call.
|
||||
|
||||
What is a system call?
|
||||
|
||||
#+begin_src bash
|
||||
man syscalls
|
||||
#+end_src
|
||||
|
||||
#+begin_quote
|
||||
The system call is the fundamental interface between an application and the Linux kernel.
|
||||
#+end_quote
|
||||
|
||||
* 13. write
|
||||
|
||||
#+begin_src bash
|
||||
man 2 write
|
||||
#+end_src
|
||||
|
||||
#+begin_src C
|
||||
write(int fd, const void *buf, size_t count);
|
||||
#+end_src
|
||||
|
||||
#+begin_quote
|
||||
write() writes up to count bytes from the buffer starting at buf to the file referred to by the file descriptor fd.
|
||||
#+end_quote
|
||||
|
||||
* 14. write
|
||||
|
||||
#+begin_src text
|
||||
write(1, "Hello, World\n", 13Hello, World
|
||||
) = 13
|
||||
#+end_src
|
||||
|
||||
File Descriptor 1 = Standard Out (inherited from terminal process)
|
||||
Hello World = Buffer
|
||||
Count = 13 bytes
|
||||
|
||||
|---+---+---+---+---+---+---+---+---+---+---+---+----|
|
||||
| H | e | l | l | o | , | | W | o | r | l | d | \n |
|
||||
|---+---+---+---+---+---+---+---+---+---+---+---+----|
|
||||
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 |
|
||||
|---+---+---+---+---+---+---+---+---+---+---+---+----|
|
||||
|
||||
* 15. Back to Nothing
|
||||
#+begin_src C
|
||||
int main(void) {}
|
||||
#+end_src
|
||||
|
||||
#+begin_src bash
|
||||
gcc exit.c -o exit
|
||||
#+end_src
|
||||
|
||||
#+begin_src bash
|
||||
strace ./exit
|
||||
#+end_src
|
||||
|
||||
* 16. Exit Code
|
||||
In C, the return type prefixes the function.
|
||||
|
||||
~main~ returns an ~int~; the default is zero (indicating no error).
|
||||
|
||||
#+begin_src C
|
||||
int main(void) {
|
||||
return 1;
|
||||
}
|
||||
#+end_src
|
||||
|
||||
#+begin_src C
|
||||
exit_group(1)
|
||||
#+end_src
|
||||
|
||||
* 17. exit_group
|
||||
#+begin_src bash
|
||||
man 2 exit_group
|
||||
#+end_src
|
||||
|
||||
#+begin_src C
|
||||
void exit_group(int status);
|
||||
#+end_src
|
||||
|
||||
* 18. Exit
|
||||
|
||||
[[file:src/exit/exit.s]]
|
||||
|
||||
* 19. Assemble, Link, Execute, Trace
|
||||
#+begin_src bash
|
||||
as exit.s -o exit.o
|
||||
|
||||
ld exit.o -o exit
|
||||
|
||||
./exit
|
||||
|
||||
strace ./exit
|
||||
#+end_src
|
||||
|
||||
* 20. x86 Assembly
|
||||
- Human readable form of machine code.
|
||||
- 1-to-1 mapping between one assembly instruction and one CPU instruction.
|
||||
- Hardware specific: e.g., x86 Assembly differs from ARM Assembly.
|
||||
- Often OS specific --> Linux System Calls != BSD, Mac, Windows system calls.
|
||||
- Different syntax formats: ATT, Intel.
|
||||
- Examples use ATT syntax.
|
||||
- What instructions? Need to consult hardware manual.
|
||||
- [[https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html][Intel x86 Developer Manual]] is ~5,000 pages long, plus errata.
|
||||
|
||||
#+begin_src bash
|
||||
lscpu
|
||||
#+end_src
|
||||
|
||||
* 21. Use Cases
|
||||
- Low-level programming (micro-controllers, operating systems)
|
||||
- Resource Constrained Hardware
|
||||
- [[https://github.com/chrislgarry/Apollo-11][Apollo 11 Guidance Computer Assembly]] (1969)
|
||||
- [[https://github.com/pret/pokered][Pokemon Red/Blue Assembly]] (1996, AA batteries)
|
||||
- Performance
|
||||
- Less runtime overhead (system calls, etc)
|
||||
- Better code than compiler (harder to do these days)
|
||||
- Control
|
||||
- Instructions not available in higher level language
|
||||
- Reverse Engineering
|
||||
|
||||
#+begin_src bash
|
||||
hexdump -C exit
|
||||
#+end_src
|
||||
|
||||
#+begin_src bash
|
||||
objdump -D exit
|
||||
#+end_src
|
||||
|
||||
* 22. Instructions
|
||||
- Represented by numbers (opcodes).
|
||||
- Describe an operation the CPU should perform, e.g.:
|
||||
- Move data in and out of registers
|
||||
- Modify register contents
|
||||
- Modify stack
|
||||
- Control program flow
|
||||
|
||||
* 23. Instruction Cycle
|
||||
- On every tick of its internal clock, the CPU:
|
||||
- *Fetches* the next instruction.
|
||||
- *Decodes* it (what operation, on what operands).
|
||||
- *Executes* the instruction.
|
||||
- Increments the instruction pointer.
|
||||
|
||||
* 24. Registers
|
||||
- Storage on the CPU (fastest storage).
|
||||
- Act as a scratchpad -- temporary variables.
|
||||
- General Purpose Registers
|
||||
- RAX, RBX, RCX
|
||||
- RSP, RBP (stack pointer, stack frame pointer)
|
||||
- Special Purpose Registers
|
||||
- RIP (Instruction Pointer)
|
||||
- RFLAGS (negative, zero, etc.)
|
||||
- It's possible to use just a portion of the register.
|
||||
#+begin_src text
|
||||
|__64__|__56__|__48__|__40__|__32__|__24__|__16__|__8___|
|
||||
|__________________________RAX__________________________|
|
||||
|xxxxxxxxxxxxxxxxxxxxxxxxxxx|____________EAX____________|
|
||||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|_____AX______|
|
||||
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|__AH__|__AL__|
|
||||
#+end_src
|
||||
|
||||
* 25. Exit++
|
||||
|
||||
[[file:src/math/math.s]]
|
||||
|
||||
* 26. Sections
|
||||
What happens when we run a program? A few things...
|
||||
|
||||
One of them: the kernel loads the executable into memory.
|
||||
|
||||
Assembly sections refer to executable's memory layout:
|
||||
|
||||
|-----------|
|
||||
| TEXT | --> Code (instructions)
|
||||
| RODATA | --> const str = "Hello, World";
|
||||
| DATA | --> var str = "Hello, World";
|
||||
| BSS | --> var str;
|
||||
| ↓ HEAP ↓ | --> (for traditional C)
|
||||
| |
|
||||
| ↑ STACK ↑ |
|
||||
|-----------|
|
||||
|
||||
* 27. Hello World
|
||||
|
||||
[[file:src/hello-world/hello-world.s]]
|
||||
|
||||
* 28. Control Flow
|
||||
Programs are either sequential, looping, or branching.
|
||||
|
||||
- CPU sets FLAGS register after instruction: e.g., result is zero, negative.
|
||||
- Jump to code based on the state of FLAGS.
|
||||
- Jump changes instruction pointer.
|
||||
|
||||
[[file:src/control-flow/control-flow.s]]
|
||||
|
||||
* 29. Stack
|
||||
RSP register points to the top of the stack.
|
||||
RBP register (typically) points to the (current) base of the stack.
|
||||
Together, they form a stack frame.
|
||||
|
||||
Instructions:
|
||||
- ~push~ :: decrements RSP, moves bytes onto stack.
|
||||
- ~pop~ :: increments RSP, moves stack bytes into register.
|
||||
|
||||
[[file:src/stack/stack.s]]
|
||||
|
||||
* 30. Functions
|
||||
Why do we use functions? Same reasons apply in Assembly:
|
||||
- Reuse
|
||||
- Organization
|
||||
- Abstraction
|
||||
- Splitting work
|
||||
|
||||
Problems:
|
||||
- How to pass arguments?
|
||||
- Registers -- which ones?
|
||||
- Stack -- what order?
|
||||
- Whose job is it to preserve or clean up registers? Caller? Callee?
|
||||
- E.g., caller saves a value in %rbx to use after function returns.
|
||||
- Callee uses %rbx and overwrites that value.
|
||||
- How to pass return value(s)?
|
||||
|
||||
* 31. Convention
|
||||
Which side of the street should we drive on?
|
||||
Either way works, both are used in practice.
|
||||
What matters is agreement on an approach.
|
||||
|
||||
[[file:static/convention.jpg]]
|
||||
|
||||
System V AMD64 ABI is calling convention for Unix x64 systems:
|
||||
- Some registers must be saved by the caller, so callee can use them.
|
||||
- Some registers must be saved by callee, if they plan to use them later.
|
||||
- Some registers used to pass arguments.
|
||||
- Stack used to pass extra or large arguments.
|
||||
- RAX and RDX are used for return values.
|
||||
|
||||
* 32. Stack Arguments
|
||||
|
||||
[[file:src/func/func.s]]
|
||||
|
||||
Each row is 8 bytes (64 bits).
|
||||
|----------------+-----------+----------------|
|
||||
| Address | Data | Stack Pointers |
|
||||
|----------------+-----------+----------------|
|
||||
| 0x7fffffffe8f8 | | |
|
||||
| 0x7fffffffe900 | 0x0 (rbp) | |
|
||||
| 0x7fffffffe908 | 0x401002 | |
|
||||
| 0x7fffffffe910 | 3 | ←rsp |
|
||||
|----------------+-----------+----------------|
|
||||
←rbp
|
||||
|
||||
* 33. Safety and Security
|
||||
|
||||
[[file:src/safety/safety.s]]
|
||||
|
||||
* 34. Review
|
||||
Where we started:
|
||||
|
||||
#+begin_src bash
|
||||
node -e ""
|
||||
#+end_src
|
||||
|
||||
- CPU processes instructions
|
||||
- Uses registers and memory (stack)
|
||||
- Control flow with jump instruction and flags register
|
||||
- Functions
|
||||
- System Calls
|
||||
- Comparison with Compiled and Interpreted Languages
|
||||
- Tradeoffs
|
||||
|
||||
* 35. Conclusion
|
||||
- Insight into how computers work.
|
||||
- Appreciation for higher level, and work done to get us here.
|
||||
- A platform to better understand things like functions, closures, APIs, pass by reference and pass by value, performance.
|
||||
- A few mysteries to leave you curious...
|
||||
|
||||
* 36. References / Further Reading
|
||||
|
||||
- [[https://www.youtube.com/watch?v=tpIctyqH29Q&list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo][Crash Course: Computer Science]]
|
||||
- Davy Wybiral, [[https://www.youtube.com/playlist?list=PLmxT2pVYo5LB5EzTPZGfFN0c2GDiSXgQe][Intro to x86 Assembly Language]]
|
||||
- [[https://www.gnu.org/software/gdb/][GDB]]
|
||||
- Jennifer Rexford, [[https://www.cs.princeton.edu/courses/archive/fall05/cos217/][Princeton COS 217: Introduction to Programming Systems]]
|
||||
- [[https://en.wikipedia.org/wiki/Structured_program_theorem][Structured Program Theorem]]
|
||||
@@ -0,0 +1,23 @@
|
||||
.data
|
||||
stop:
|
||||
.int 3
|
||||
|
||||
.text
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
mov $0, %rcx
|
||||
loop:
|
||||
cmp stop, %rcx
|
||||
# If equal, change RIP to exit.
|
||||
je exit
|
||||
inc %rcx
|
||||
# Unconditionally change RIP to loop.
|
||||
jmp loop
|
||||
exit:
|
||||
# Try this: comment out the following three instructions.
|
||||
# What happens if you assemble and link? Why?
|
||||
# Think about RIP and the Fetch-Decode-Execute cycle.
|
||||
mov $231, %rax
|
||||
mov %rcx, %rdi # Exit code -- let's see the value of %rcx.
|
||||
syscall
|
||||
@@ -0,0 +1,3 @@
|
||||
int main(void) {
|
||||
// return 1;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
.text
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
# Store system call 1 in the EAX register.
|
||||
# mov = instruction
|
||||
# $1, %eax = operands
|
||||
# %eax = register
|
||||
mov $1, %eax
|
||||
|
||||
# Store the exit code 0 in the EBX register.
|
||||
mov $0, %ebx
|
||||
|
||||
# Call interrupt handler 0x80 (128) -- on Linux, this is the kernel.
|
||||
# Kernel will take over and see 1 in %eax, 0 in %ebx.
|
||||
int $0x80
|
||||
@@ -0,0 +1,41 @@
|
||||
.text
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
# Store 3 on the stack.
|
||||
push $3
|
||||
# call stores return address on the stack.
|
||||
call square
|
||||
# Use the return value, in %rax, as our exit status number.
|
||||
mov %rax, %rdi
|
||||
mov $231, %rax
|
||||
syscall
|
||||
|
||||
# square expects an integer on the stack, 16 bytes down from RBP.
|
||||
# The squared value is returned on %rax.
|
||||
square:
|
||||
# Function Prologue --> {
|
||||
push %rbp
|
||||
mov %rsp, %rbp
|
||||
|
||||
# Retrieve the parameter from the stack.
|
||||
# 8 bytes down from RSP is the return address from square.
|
||||
# 8 bytes down from that is the parameter to multiply.
|
||||
mov 16(%rbp), %rax
|
||||
mul %rax
|
||||
|
||||
# Function Epilogue --> }
|
||||
mov %rbp, %rsp
|
||||
pop %rbp
|
||||
# ret pops and returns to address on stack.
|
||||
ret
|
||||
|
||||
# |----------------+-----------+----------------|
|
||||
# | Address | Data | Stack Pointers |
|
||||
# |----------------+-----------+----------------|
|
||||
# | 0x7fffffffe8f8 | | |
|
||||
# | 0x7fffffffe900 | 0x0 (rbp) | |
|
||||
# | 0x7fffffffe908 | 0x401002 | |
|
||||
# | 0x7fffffffe910 | 3 | ←rsp |
|
||||
# |----------------+-----------+----------------|
|
||||
# ←rbp
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
printf("Hello, World\n");
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
.data
|
||||
str:
|
||||
.ascii "Hello, World\n"
|
||||
|
||||
.text
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
mov $1, %rax # 64-bit system call number for sys_write.
|
||||
mov $1, %rdi # stdout.
|
||||
mov $str, %rsi # Address of the buffer.
|
||||
mov $13, %rdx # Number of bytes to write.
|
||||
syscall
|
||||
|
||||
mov $231, %rax # 64-bit system call number for exit_group.
|
||||
mov $0, %rdi # Exit code.
|
||||
syscall
|
||||
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
now := time.Now()
|
||||
for i := 0; i < 1000; i++ {
|
||||
fmt.Println(i)
|
||||
}
|
||||
fmt.Printf("took %v\n", time.Since(now))
|
||||
|
||||
now = time.Now()
|
||||
var str strings.Builder
|
||||
for i := 0; i < 1000; i++ {
|
||||
str.WriteString(fmt.Sprintf("%d\n", i))
|
||||
}
|
||||
fmt.Println(str.String())
|
||||
fmt.Printf("took %v\n", time.Since(now))
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
.text
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
mov $0, %rax # rax = 0
|
||||
inc %rax # rax++
|
||||
dec %rax # rax--
|
||||
add $7, %rax # rax += 7
|
||||
sub $5, %rax # rax -= 5
|
||||
|
||||
# At this point, rax = 2.
|
||||
# Multiplication cannot use immediate addressing.
|
||||
# So we store the multiplier in %rbx.
|
||||
# mul assumes the multiplicand is in %rax.
|
||||
# The product is also stored in %rax.
|
||||
mov $12, %rbx
|
||||
mul %rbx # rax *= 12
|
||||
|
||||
# rax = 24.
|
||||
# Division also cannot use immediate addressing.
|
||||
# Store the divisor in %rbx.
|
||||
# div assumes the dividend is in %rax.
|
||||
# The quotient is also stored in %rax.
|
||||
mov $3, %rbx
|
||||
div %rbx
|
||||
|
||||
# rax = 8
|
||||
# Let's use this as our exit code.
|
||||
# We're using 64-bits now -- the exit code should be in %rdi.
|
||||
mov %rax, %rdi
|
||||
mov $231, %rax
|
||||
syscall
|
||||
@@ -0,0 +1,47 @@
|
||||
.data
|
||||
romeo:
|
||||
.ascii "What's in a name? That which we call a rose,\n"
|
||||
juliet:
|
||||
.ascii "By any other name would smell as sweet.\n"
|
||||
.ascii "\n"
|
||||
stop:
|
||||
# Try changing this number. What happens? Why?
|
||||
# Hint: try 0, 1, 11, 12, 21, 22.
|
||||
.int 12
|
||||
|
||||
.text
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
# Print the string before it's messed up.
|
||||
mov $1, %rax # sys_write.
|
||||
mov $1, %rdi # stdout
|
||||
mov $romeo, %rsi # address
|
||||
mov $86, %rdx # count bytes
|
||||
syscall
|
||||
|
||||
# Setup.
|
||||
lea romeo, %rax # Load the starting address of string into %rax.
|
||||
mov $0xb98c9ff0, %ebx # 🌹 = b98c9ff0 (4 bytes)
|
||||
mov $0, %rcx # Set the initial counter.
|
||||
|
||||
# Overwrite the strings with 🌹.
|
||||
# Increment the address in %rax by four bytes on each iteration.
|
||||
loop:
|
||||
mov %ebx, (%rax) # Copy
|
||||
inc %rcx # increment our counter
|
||||
add $4, %rax # Try changing this to sub. What happens? Why?
|
||||
cmp stop, %rcx
|
||||
jne loop
|
||||
|
||||
# Print the string after it's been messed up.
|
||||
# NB: nothing stopped us from overwriting past the "romeo" string.
|
||||
mov $1, %rax # sys_write
|
||||
mov $1, %rdi # stdout
|
||||
mov $romeo, %rsi # address
|
||||
mov $85, %rdx # count bytes
|
||||
syscall
|
||||
|
||||
mov $231, %rax # 64-bit system call number for exit_group.
|
||||
mov $0, %rdi # Exit code.
|
||||
syscall # Call the kernel.
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
.text
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
# Push some 64-bit integers onto the stack.
|
||||
push $3
|
||||
push $2
|
||||
push $1
|
||||
|
||||
# Let's print "Hey\n", using the stack.
|
||||
# man ascii
|
||||
# |---------+----+-----+-----+----|
|
||||
# | ASCII | H | e | y | \n |
|
||||
# |---------+----+-----+-----+----|
|
||||
# | Decimal | 72 | 101 | 121 | 10 |
|
||||
# |---------+----+-----+-----+----|
|
||||
# | Hex | 48 | 65 | 79 | 0A |
|
||||
# |---------+----+-----+-----+----|
|
||||
|
||||
# x86 is little-endian: it stores the least significant byte
|
||||
# of a multi-byte value at the lowest memory address.
|
||||
# Since the write system call reads memory from low to high address,
|
||||
# we put the first character ('H' = 0x48) in the least significant byte.
|
||||
# <-- high low -->
|
||||
# \n y e H
|
||||
push $0x000000000A796548
|
||||
|
||||
# %rsp points to the top of the stack (the lowest address of our push,
|
||||
# where 'H' is stored).
|
||||
# We can pass this address to the kernel.
|
||||
# write will start at 'H', and work up to '\n'.
|
||||
mov $1, %rax # write
|
||||
mov $1, %rdi # stdout
|
||||
lea (%rsp), %rsi # address of buffer
|
||||
# Try changing the number of bytes to 9, 17, 25.
|
||||
# Then call the program with strace.
|
||||
# You'll be able to see the previous numbers we pushed onto the stack.
|
||||
mov $4, %rdx # number of bytes
|
||||
syscall
|
||||
|
||||
# Remember, the stack is LIFO.
|
||||
pop %rdi # pop the "Hey\n" string
|
||||
pop %rdi # pop 1
|
||||
pop %rdi # pop 2
|
||||
|
||||
# We'll use the last popped value in %rdi as our exit code.
|
||||
mov $231, %rax
|
||||
syscall
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 314 KiB |
Reference in New Issue
Block a user