Making System Calls

Making System Calls in Minix

Here's the basic idea of how system calls are performed. The system I'm using for an example is Minix, because it's simple (I spent some time trying to figure out how it works in Linux, and it was such a mess of obscure #define goodies, inline functions, and stuff that I finally decided that if I ever did work it out, it would take several lectures to describe it...).

Something to keep in mind with Minix is that, because of its microkernel structure, it actually only has three system calls: send, receive, and sendrec. The idea is, if you want a subsystem to do something for you, you send a message to the subsystem you have in mind. So, the microkernel itself only recognizes calls to send a message, to receive a message, and to send a message and wait for a reply. Of these three, user processes are only allowed to use the last one (a kernel process typically just calls receive and waits for a message to come in. When a message arrives, it services the request, and sends a response back).

So here is how a ``read'' takes place. First, the user side.

  1. The user calls read, which is a function in Minix's POSIX library. The code for the read function is in http://www.CS.NMSU.Edu/~pfeiffer/classes/474/minix/src/lib/posix/read.c
  2. The read function calls a function called callm1 (make a system call using a message of type 1). The source code for callm1 is in http://www.CS.NMSU.Edu/~pfeiffer/classes/474/minix/src/lib/other/call.c. Horrifyingly enough, the struct that contains the message that's actually sent is a global variable defined in http://www.CS.NMSU.Edu/~pfeiffer/classes/474/minix/src/lib/other/message.c and http://www.CS.NMSU.Edu/~pfeiffer/classes/474/minix/include/minix/type.h! callm1 calls callx
  3. callx, in turn, calls sendrec. This is written in assembly language (so it's the only part of the user-side part of doing a system call that's specific to the PC). It's in http://www.CS.NMSU.Edu/~pfeiffer/classes/474/minix/src/lib/ibm/sendrec.s. It puts the necessary parameters in registers, and requests a software interrupt. When the interrupt returns, the whole sequence of calls will unwind.

Second comes the code for the handler in the operating system.

  1. The vector for the system call interrupt is set up as part of the boot process. The code that does this is in http://www.CS.NMSU.Edu/~pfeiffer/classes/474/minix/src/kernel/main.c. It sets a procedure called s_call as the vector.
  2. s_call is another assembly language routine, located in http://www.CS.NMSU.Edu/~pfeiffer/classes/474/minix/src/kernel/mpx.x (along with all the other low-level interrupt handlers). It saves the processor state, and calls sys_call.
  3. sys_call is defined in http://www.CS.NMSU.Edu/~pfeiffer/classes/474/minix/src/kernel/proc.c It checks to make sure the message is going to something in the operating system and that it is a sendrec message, and passes it on.

Last but not least, the code in the filesystem that interprets the read, in http://www.cs.nmsu.edu/~pfeiffer/classes/474/minix/src/fs/main.c

Making System Calls in Linux

In Linux, the first step is very similar: the user program calls a read() function in the standard C library; this function does little more than put a system call number (the system call number for read() is three) in the eax register and invoke a software interrupt. This lands it in some assembly code that you can find in /usr/src/linux/arch/i386/kernel/entry.S at the line containing ENTRY(system_call). This code simply saves all the registers, makes the sure the call number is within the range of defined system call numbers, and calls the routine to service the call. After the call has been serviced, there is quite a bit of code doing things like handling pending signals, and then it returns from the interrupt handler.

Note that there is a lot less work here!


Last modified: Wed Sep 6 10:48:36 MDT 2006