|   blog   |   blog_archive   |   about   |   contact   |   tutoring   |

$ cat blog/*

wayless_presentation.md:                            2019-11-10 [permalink] [raw]

Wayless: Presenting for PDXRust

I had the opportunity to talk about Wayless, an operating system kernel I've been working on, at the Portland Mozilla offices last Wednesday. The slides from the talk are up at https://finzdani.net/rust_osdev.pdf

Thanks everyone who made it out! I'm excited I got the chance to speak about what I've been working on. Here's the links from the last slide of my presentation:

Phil Opperman's BlogOS: https://os.phil-opp.com

OSDev Wiki: https://wiki.osdev.org

AMD's x86_64 Programmer's Manual, Volume 2: https://www.amd.com/system/files/TechDocs/24593.pdf

Muchless: https://gitlab.com/waylon531/muchless

Wayless: https://gitlab.com/wayless/wayless


threads.md:                                         2019-11-10 [permalink] [raw]


Here's a short tutorial on what threads are and why they're important. This is inspired by a question I got on Discord, I figured that I might as well write up a long-form response as it might help other people. Hopefully there'll be more blog posts incoming in the near future :)

Before multitasking

Early personal computers were only able to run a single program at a time. This affected early operating systems like MS-DOS--whenever you run a program in MS-DOS it gets sole control of your CPU. This works perfectly fine when the only way to use the computer is through a command-line interface--you'll type a program's name, it'll run for a bit, and eventually it'll exit and once again MS-DOS will be in control of your CPU.

However, this is kind of limiting. You can't edit a text file and listen to your collection of MIDI music files at the same time. You can't run multiple different command-line sessions. And worst of all, if you have a graphical interface to your computer, you can't have multiple windows open at the same time.


Single-task operating systems had some severe limitations. Pretty much none of these operating systems are around today: most modern systems are multitasking.

Multitasking operating systems have the ability to run multiple programs at the same time. Not only does this now let you run programs in the background of your computer, it now means that you can have your browser open at the same time as your IDE. It's probably hard to imagine a computer that doesn't let you run multiple programs.

To accomplish this your operating system has to do a bit of work, your processor can still only run a single program at one time. To give the illusion of multiple programs running simultaneously the operating system has to rapidly switch between all currently running programs, called threads, letting each run for a fraction of a second at a time. This process is called context switching, specifically thread context switching. However, the operating system has to figure out exactly when to switch threads. There's two major ways to do this, through cooperative multitasking and preemptive multitasking.

Cooperative multitasking is named as such because threads are expected to cooperate with each other to make thread switches happen. Each thread can yield control to the operating system, letting the operating system know that it's ready for a thread switch to happen. Yielding typically happens through a system call provided by the operating system. Each thread is expected to yield withing a reasonable time, and this method will get great performance. However, as soon as you get a malicious process that refuses to yield your computer is frozen, running that malicious process until the end of time (or a hard reset).

Using preemptive multitasking threads will automatically be switched after a specified period of time, typically somewhere between 1ms and 100ms. The operating system will use a timer to generate periodic interrupts, stopping the current process and letting the operating system handle a switch into the next process. Typically the operating system will also let threads yield in addition to automatically preempting them. Preemptive multitasking fixes the issue of malicious program locking up the computer: now if a program tries to run forever without yielding it'll just get preempted by the operating system, letting other programs run like normal.


A thread of execution represents part of a running program. Some programs may run as a single thread, some may run using multiple threads. Many operating systems also make a distinction between threads and processes, but I won't here. To run, each thread needs its own set of registers, as well as its own virtual memory space. Each thread keeps track of its own instruction pointer, a pointer to the next assembly instruction to execute. Depending on the operating system some extra information might need to be stored with each thread (Wayless keeps track of a capability space for each thread). To switch between threads the operating system saves the values of the instruction pointer and every register. The operating system calls its scheduler, which figures out what thread to run next. Once the operating system knows what thread to run it loads the values of all the registers from the new thread, and switches from the old address space to the new space. Then the operating system sets the instruction pointer to that of the new thread. Now the computer is all set up to start executing the new thread, right where the thread left off.


The scheduler keeps track of all of the threads and decides what order to run them in. There's a lot of different scheduling algorithms, so I'll just cover a few here. One of the simplest ways to schedule threads is using a round robin scheduler. This scheduler uses a queue of threads, which can be implemented using a linked list. When the operating system switches threads it saves the current thread to the end of the queue. Then, the next thread to run is dequeued from the front of the queue. This ensures that every thread gets ran, and that threads aren't able to starve the system of CPU time. It also allows for a simple implementation. However, it doesn't allow for priority levels and potentially limits the responsiveness of high-priority threads.

A priority-based round robin scheduler adds priority levels. A different round-robin queue exists for each priority level. Processes from higher priority queues get ran first--each thread from the highest priority level is run, then each thread from the next-highest priority level, and so on. This scheduling algorithm extends the round robin scheduler, adding slightly to its complexity. Now programs that need to be highly responsive, like graphical programs, are guaranteed to run before lower priority programs.

Multicore Systems

Some computers have multiple cores, or multiple physical CPUs. A thread can run on every single core, potentially providing a huge increase in speed. As each program runs using at least one thread, this automatically provides a benefit when running many different programs simultaneously. However, breaking your program up into multiple threads usually takes a bit of work (modulo programming language). If you can break your program up into multiple threads then each of those threads can run in parallel, given enough cores, giving a massive boost to performance. If your program is only a single thread then it won't be able to take advantage of the multiple cores.

Further Reading




gdb.md:                                             2019-11-21 [permalink] [raw]

uelen's GDB cheatsheet

Getting Started

If you've never run GDB before, you can start it up with the following command:
gdb <program>

If you've been compiling to a.out then you'd start GDB up with
gdb ./a.out

Make sure to add the -g flag when compiling your program! This would look something like
g++ -g my_program.cpp

And here's some conventions I'm using:

Some Tips

There's a few tricks I've learned that make GDB easier to use. First of all, you don't actually need to type out the full command name to run a command. You can type the first few characters of a command, or potentially even a single character. This means instead of typing layout you can type lay, and instead of typing break you can type b.

Sometimes you want to run the same command a bunch of times in a row. Typing next a bunch of times is a pain. However, if you enter an empty command and just hit Enter then GDB will repeat the last command. Instead of typing next a bunch of times, you can type it a single time and then repeatedly hit the Enter key.

The Commands

Debugging Assembly

This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. If you're interested in modifying this document you can obtain the original markdown at https://finzdani.net/gdb.md


forward.txt:                                        2019-01-23 [permalink] [raw]
Local mail is a pretty cool concept that has all but died out. Because
it is centered around one system mail gets delivered directly to each user,
letting users customize how they recieve mail. One of the ways they can
do this is though the .forward file.


The .forward file:

sendmail will look for a file called .forward in your home directory.
This file contains a list of entries to forward messages to. For example,
if my .forward file consisted of:


then all messages sent to me (uelen) would get sent to steven, paul,
and then a copy would finally get sent to me. Interestingly, the .forward
file supports commands in addition to just usernames. You can stick a pipe
in your .forward file, followed by a command, to pipe all of your email
messages to a specified program. My .forward file looks like this:


I pipe all of my messages to procmail, which takes them out of the mail
spool and sends them to my Maildir. mutt, my preffered email client, can
then interact directly with this mail. I can also get rid of the "You have
new mail" message without actually deleting my mail :P

I'll cover procmail in a future textfile