OS Interface: Syscall Interrupts

CS 321 Lecture, Dr. Lawlor, 2006/01/25

The Silberschatz book documents system calls in section 2.3.

Interrupts as a cry for help

The OS just doesn't care what you're working on.  It doesn't hover over your shoulder, watching your reads and writes to registers or memory.  It's got better things to do.  Instead, if you want the OS's attention, you've got to do something that can't be ignored.  The standard unignorable operation is to throw an "software interrupt", which you can do with the special "INT" (generate software interrupt) instruction.

Hardware interrupts are used by the hardware to get the CPU's attention--for example, the IDE controller will raise an I/O interrupt when a read is complete.  The OS (the BIOS, or Windows, or Linux) handles all interrupts, both hardware and software.  We'll talk a lot more about hardware interrupts when we talk about I/O.

How interrupts work as system calls ("syscalls")

So the standard way to ask the OS to do something is to issue a special interrupt, normally called a system call or "syscall":
  1. Load up the CPU with information about what you want done.  Usually there's some sort of "selector code" that tells the OS what you want done (on Linux, the "syscall number", which goes into eax), and then a set of arguments.  The selector and arguments can be stored:
  2. Call the OS, by issuing an interrupt.  You could also imagine a machine where you ask the OS to do stuff by just segfaulting--accessing a special off-limits memory location.
  3. The OS's "interrupt service routine" (just normal code, called by the CPU when an interrupt happens) then reads the selector code from your registers, does what you want done, and then returns control back to you. 
  4. The OS might have stashed return information (like an error code) in registers, the stack, or elsewhere.  As with all this stuff, you've got to read each OS's docs to figure out how it works.
The PC BIOS uses interrupts 0x10 and 0x16.  MS-DOS mostly uses interrupt 0x21.  Ralph Brown's Interrupt List is the definitive reference for all BIOS and MS-DOS interrupt functions.

Windows XP now uses the special SYSENTER x86 instruction for system calls.  Windows 2000 and earlier versions of NT used INT 0x2e to access OS.

Nowadays, you almost never make system calls directly, since the system call interfaces invariably require ugly assembly code.  Instead, you call nice system libraries that hide the ugly assembly.

Syscall examples--BIOS

HW1 problem 2 is a good example of a canonical use of system calls, in this case to access the BIOS screen output functionality at Interrupt 0x10 ah==0x0e.
	mov bx, 0x0007 
mov ax, 0x0e4E ; low bits == ASCII 'N'
int 0x10 ; "output character" interrupt
You can also accomplish the exact same thing like this:
	mov bl, 0x07 ; Color (only in color modes)
mov bh, 0x00 ; Page number
mov al, 'N' ; character to output
mov ah, 0x0e ; Function 0x0e: output character to screen
int 0x10 ; generic BIOS interrupt
Remember that "al" is the low 8 bits of "ax", so if you've set ax to 0xff00, al is zero; if you've set ax to 0x00ff, al is 0xff.  You can willy-nilly read and write from al, ah, and ax, and everything will still correspond.

This system call doesn't return anything.  But the wait-for-keystroke interrupt returns the key in ASCII in register al.  There's also a hardware "scan code" in ah.
	xor ax,ax  ; set ax to zero
int 0x16 ; Wait for a keypress
; now al contains the keyboard key that was pressed...
You can stick these two togther to just echo characters from the keyboard to the screen.

Syscall example--Linux

Konstantin Boldyshev has a good writeup and examples of Linux, BSD, and BeOS x86 syscalls, and a list of common Linux syscalls.  He uses NASM for the examples. Here's a slightly cleaned up version of his Linux example.
(Executable NetRun Link)
section .rodata
msg db 'Wazzup?',0xa ; our little string, followed by a newline
len equ $ - msg ;length of our string

section .text
global foo ; the foo routine is called from outside, by netrun
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
; Kernel call return value is in eax-- it'll do as a function return code.