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":
- 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:
- Inside
the interrupt instruction itself. The x86 "INT"
instruction can initiate 256 different interrupts. The Motorola
68000 had a 16-bit "OS Trap" instruction 0xA??? that left 12 bits for
the "trap number", which could be any of 4096 different values (and the
old MacOS used a substantial fraction of these for different routines!).
- Inside processor registers. It's pretty common to store a selector code in register eax.
- On the runtime stack, just like arguments to a normal subroutine.
- 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.
- 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.
- 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
foo:
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.
ret