Dusken OS
Inside Dusken OS

How the kernel actually works.

Follow a key press, a web page, a saved file and a frame of video through Dusken's own code, then watch the scheduler, memory and compositor at work. Every step names the real function that does it, and every number is read from the source.

20,334
lines of kernel code
54
kernel source files
34
Dusken system calls
144
Linux system calls answered
8
processors at most
1,000
timer ticks a second
System map

Follow a journey through the system

Each box is a part of Dusken with the source files that make it and their size. Pick a journey to watch it travel from layer to layer, or click a step to jump to it.

Programsring 3
Web browseruser/web.c, html.c, css.c, layout.c
TLS 1.3 and cryptouser/tls.c, crypto.c, x509.c
Dusken programsuser/libc.c, gfx.c
Linux programsBusyBox, Node.js, GTK, Qtmusl or glibc
System callsthe gate
Dusken system callsint 0x80, svc #0
Linux system callssrc/linux.c, via #UD
Kernelone lock
Corearch.c, main.c, smp.c
Processes and schedulersrc/proc.c
Window managersrc/wm.c
Graphics and fontsgfx.c, ttf.c
Input and layoutssrc/input.c
TCP/IPsrc/net.c
Files and FAT32files.c, fat.c
Disk cachesrc/disk.c
X11 serversrc/x11.c
Driversmostly polled
PS/2 keyboard, mousesrc/drivers.c
USB (xHCI)src/usb.c
Network cardsnic.c, tulip.c, rtl.c, netvsc.c
Disk controllersata.c, ahci.c, nvme.c, storvsc.c
Displaydisplay.c, synthvid.c
Soundsrc/sound.c
Hyper-V VMBushyperv.c, hvutil.c
Hardwareor a VM
Processorsup to 8
KeyboardPS/2 or USB
NetworkEthernet
DiskIDE to NVMe
Screenframebuffer

A key press

From a key going down to the program that has the focus.

  1. A key goes down. A PS/2 keyboard raises interrupt 1.
  2. kbd_irq reads the scan code from port 0x60 and turns it into a USB key number. A USB keyboard is read by usb_poll instead.
  3. isr_dispatch: the interrupt enters through vector 33, and the processor takes the kernel lock before running the handler.
  4. key_char applies the keyboard layout (UK by default) and kbd_push queues the key, with the modifiers held, in a 64-key ring.
  5. The desktop's loop hands it to wm_key, which finds the focused window.
  6. progwin_key puts a key event in that program's 64-event ring and wakes it with proc_wake.
  7. pick: the round-robin scheduler runs the woken program at the next switch.
  8. The program was waiting in win_event (system call 11, SYS_WIN_EVENT); it returns with the key.
  9. The program reacts and draws its answer, which starts the frame journey.

Loading a web page over HTTPS

Dusken's own browser, network stack and TLS, end to end.

  1. fetch_as builds an HTTP/1.1 request. A kept-alive connection to the same site is reused.
  2. System calls 12 and 13 (SYS_RESOLVE, SYS_CONNECT) ask the kernel for an address and a connection.
  3. net_resolve sends a DNS query over UDP to the server DHCP gave; net_connect sends a TCP SYN.
  4. nic_send hands the frame to the network card: Intel e1000, Hyper-V, virtio, Tulip or Realtek.
  5. Out on the wire, and the answers come back.
  6. No interrupts: net_poll calls nic_poll, which pulls each received frame off the card's ring.
  7. tcp_rx puts the bytes in the socket's 64 KiB receive buffer for SYS_RECV.
  8. tls_connect: TLS 1.3 inside the browser. X25519 key exchange, ChaCha20-Poly1305 or AES-128-GCM, and the certificate chain checked against trusted roots.
  9. html_parse, css_compute and layout_page build the page; QuickJS runs its scripts; pictures are decoded at the size they are shown.
  10. System call 10 (SYS_WIN_PRESENT) hands the finished pixels to the kernel.
  11. wm_prog_present copies them and marks the changed area as damaged.
  12. gfx_present copies only the rows that changed to video memory.
  13. The page is on screen.

Saving a file

Ctrl+S in the Editor, down to the disk.

  1. ed_save: the Editor is built into the window manager, so it calls the file layer directly. Programs use system call 6, SYS_FWRITE.
  2. file_write sends it to fat_write, which allocates and writes a new cluster chain first.
  3. Then the old chain is freed, the directory entry (with its long name) added, and fat_flush writes the FAT and FSInfo sectors.
  4. disk_io writes straight through to the disk and updates any cached copy. Reads come from a 4-way cache of 4 KiB blocks (16 MiB on 64-bit PCs).
  5. The driver sends the sectors and polls for the result: IDE, SATA, NVMe, USB, virtio or Hyper-V storage.
  6. Saved, and still there after a restart.

Drawing a frame

How pixels get from a program to the screen without redrawing everything.

  1. A program draws into its own pixel buffer.
  2. win_present makes system call 10, SYS_WIN_PRESENT.
  3. wm_prog_present copies the pixels and calls damage. Up to 16 rectangles are kept; two merge only if that wastes no area.
  4. wm_tick runs compose at most every 16 ms: each damaged rectangle is redrawn from the wallpaper up, clipped by gfx_region.
  5. gfx_present compares the new picture with the last one row by row and copies only the changed rows to video memory.
  6. The display driver is told what changed: a VMware SVGA update, a Hyper-V dirty message or a virtio-gpu transfer. The framebuffer is write-combined.
  7. On screen.

A Linux program asks for something

How unmodified Linux programs run on a kernel that isn't Linux (64-bit PCs).

  1. A Linux program runs the syscall instruction, bytes 0F 05, to ask for, say, write.
  2. Dusken never switches that instruction on, so the processor raises an invalid-opcode fault, vector 6.
  3. isr_dispatch takes the kernel lock and sends the fault to proc_linux_trap.
  4. proc_linux_trap checks that this is a Linux program and that the bytes really are 0F 05, then steps past them.
  5. linux_syscall answers with Linux's own numbering: 144 calls, from read and mmap to clone, futex and epoll.
  6. A write to a file lands in a shared file buffer that reaches the disk on fsync or the last close.
  7. linux_deliver delivers any pending signal on the way out.
  8. The answer is back in rax, exactly where Linux would put it.

Starting up

From the boot loader to the desktop, with the lines Dusken really prints.

  1. Limine, from a USB stick, or QEMU loads the kernel at 16 MiB.
  2. _start: on 64-bit PCs a 32-bit stub checks for long mode, maps the first 4 GiB and switches to 64-bit before calling kmain.
  3. arch_init: descriptor tables, interrupt controller, a 1,000 Hz timer, and a clock calibrated from the time stamp counter.
  4. paging_init and heap_init: four-level paging, one kernel heap below 4 GiB for devices and one above it.
  5. proc_init sets up the process table, with the desktop as task 0.
  6. gfx_init and display_init find the screen and switch on write-combining.
  7. kbd_init and mouse_init install the keyboard and mouse interrupts.
  8. usb_init walks the xHCI controller for keyboards, mice, hubs and drives; hv_init joins Hyper-V's VMBus when there is one.
  9. The disk drivers probe in turn and the first disk labelled DUSKEN is mounted as FAT32.
  10. net_init finds a network card and starts DHCP.
  11. smp_init wakes the other processors with INIT and STARTUP messages; each climbs from 16-bit real mode to 64-bit through a trampoline at 0x8000.
  12. The kernel's loop becomes the desktop: polling devices, drawing the screen and running programs.
Processors

Many processors, one kernel lock

Programs run side by side on every processor, but only one processor is inside the kernel at a time. The orb is that lock. A program is switched out after 10 timer ticks of its own code; a tick that lands in kernel code never interrupts it, and a switch that can't get the lock waits for the next tick.

Processors Speed
A program's own code In the kernel, holding the lock Waiting for the lock Idle (halted) Switched at the end of a time slice

A simulation that follows the rules in src/proc.c and src/arch.c with made-up programs, slowed down; it is not a recording of a real machine. The desktop (task 0) is the kernel's own loop and only runs on the first processor.

Memory

Every program lives at 511 GiB

On a 64-bit PC each program is built to run in the last gigabyte below 512 GiB. Switching programs doesn't reload the page tables: the kernel rewrites one entry on that processor, pdpt[511], to point at the program's memory, and flushes the TLB. Watch the arrows follow the simulation above.

Not to scale. Below 4 GiB, kmalloc serves drivers that hand addresses to hardware; above it, kmalloc_big holds program memory and screen buffers. On 32-bit PCs programs live at 1 GiB with a page directory each; on ARM the same 511 GiB slot is l1[511].

Screen

Only what changed reaches the screen

The compositor keeps a list of damaged rectangles, redraws just those, then compares each row with what is already on screen and copies only the rows that differ. A dragged window's pixels are slid, not redrawn. This is the same method as src/wm.c and src/gfx.c, running live in your browser.

Damaged rectangle Slid, not redrawn Row copied to video memory Row compared, unchanged