This document explains a way to add a system call to linux kernel. A system call is the standard way an OS service is exported to a user program. For example, to provide users a new semaphore like synchronization method, some system calls need to be provided to access it. Likewise, a system call may be used to give users access to internal information of a file system such as superblock or inodes. The following description is based on linux kernel version 2.2.14 on i386 machine architecture. Also, it is assumed that readers are familiar with Building Linux Kernel.
A system call cannot be called directly from a user process. Instead, they
are called indirectly via an interrupt and looked up in an interrupt table.
Thus, when you define a new system call you insert a new entry in this table.
You do this by editing the file linux/arch/i386/kernel/entry.S
.
Inside, you should see lines like:
.data ENTRY(sys_call_table) .long SYMBOL_NAME(sys_ni_call) /* 0 */ .long SYMBOL_NAME(sys_exit) .long SYMBOL_NAME(sys_fork) ... .long SYMBOL_NAME(sys_vfork) /* 190 */After the "sys_vfork" line, add your entries for your new system calls, with the words "sys_" prepended. For example, you might add the following line for your new system call "myservice":
.long SYMBOL_NAME(sys_myservice) /* 191 */
You also need to generate a system call "stub" so that an ordinary
user program can invoke your system call. You do this by editing the
file linux/include/asm/unistd.h
where you will find lines
like:
/* * This file contains the system call numbers. */ #define __NR_exit 1 #define __NR_fork 2 ... #define __NR_vfork 190You should add
#define
s for your new system calls at the
end, with the prefix "__NR_" in front of it. For example, you might
add the line:
#define __NR_myservice 191
After inserting your new system call entry in the interrupt table and
preparing a stub for it, you will need to define (or implement) the
system call. It will be easiest to have the system call definitions in your
own source code files, say myservice.h
and myservice.c
.
In general, header files for machine architecture independent system calls
and functions are kept under linux/include/linux/
and machine
architecture dependent ones are kept in linux/include/asm/
.
Therefore, it would be a good idea to follow this convention. For example,
the header file for the system calls for your new synchronization method, of
which the implementation is machine architecture specific, would be
placed in linux/include/asm/
, while the header file for your
machine architecture independent system call that access the superblock of
one or more of your file systems would be placed under
linux/include/linux
.
The place for actual implementation file (myservice.c
in
this example) could vary. For example, if you are implementing a new
process synchronization method, linux/ipc/
would be the best
place for it. If you are implementing a file system related one,
linux/fs/
would be the best place.
Remember that you will need to modify the Makefile
in the
directory you placed your .c file so that your code gets compiled and linked
in properly. Modify the Makefile line to have a .o
of your
source code. For example, adding myservice.o
:
O_OBJS += ... myservice.oThe rest of the Makefiles can remain untouched (Makefile changes will be similar if you choose to add your code elsewhere).
In order to be linked properly, your system calls need the word "asmlinkage" prepended to their function header and "sys_" prepended to the name. For example, you would have:
asmlinkage int sys_myservice(int arg1, char* arg2) { /* implementation of myservice */ }Also, you will have to have
#include <linux/linkage.h>
so the compiler will recognize the word "asmlinkage".
Lastly, the user "stub" can be automatically generated so that a
user program can use your system call. There are some macros defined
for this in <linux/unistd.h>
. The format
is "_syscallN(return type, function name, arg1 type, arg1 name ...)"
where "N" is the number of parameters. For example, you
might have the line:
_syscall2(int, myservice, int, arg1, char*, arg2);to generate the stub (in this case, the 2 is for 2 arguments). Note, that your call to generate the stub (as above) should be put it in your header file (
myservice.h
). Also, you need to
#include <linux/unistd.h>
in myservice.h
to make this work. A user program could then just call
myservice()
as they do other system calls.
Here is an example myservice.h
and
myservice.c
, assuming myservice.h
is under
linux/include/linux/
myservice.h |
#ifndef __LINUX_MYSERVICE_H #define __LINUX_MYSERVICE_H #include <linux/unistd.h> #include <linux/linkage.h> _syscall2(int, myservice, int, arg1, char*, arg2); #endif |
myservice.c |
#include <linux/myservice.h> asmlinkage int sys_myservice (int arg1, char* arg2) { return(1); } |
In case you need to have a system global variable that your
new system calls will use to keep states of some kind, which would
be the case for implementing a new process synchronization
primitive, it might be necessary to initialize the variable at system
bootup time. In this case, you need to add your own init function (in
your source file) with return type void
followed by a
keyword __init
. Remember to include
__init
macro be recognized. Then, you need to add two
lines to linux/init/main.c
to make your kernel invoke
your initialization function at bootup. For example:
myservice.h |
... #include <linux/init.h> ... void __init myservice_init(void); |
myservice.c |
... void __init myservice_init(void) { /* initialize global variables used by myservice */ } |
linux/init/main.c |
... /* Around line 89 */ extern void myservice_init(void); ... /* Around line 1352 within "asmlinkage void __init start_kernel(void)" */ myservice_init(); |
Here is a few basic kernel library functions that might be
useful. Refer to "man" pages for a detailed information. Note that
you should use care to use cli()
and sti()
,
since misuse of them can freeze your kernel with no error
messages.
printk() | NAME printk - print messages to console log SYNOPSIS #include <linux/kernel.h> int printk(const char*fmt, ...) |
kmalloc(), | NAME kmalloc, kfree - allocate and free pieces of memory SYNOPSIS #include <linux/malloc.h> void * kmalloc (size_t size, int priority); void kfree (void * __ptr); |
cli(), | NAME cli, sti - disable/enable interrupts SYNOPSIS #include <asm/system.h> extern void cli() extern void sti() |
get_user(), | NAME get_user, put_user, copy_from_user, copy_to_user - copy data between kernel space and user space SYNOPSIS #include <asm/uaccess.h> err = get_user ( x, addr ); err = put_user ( x, addr ); bytes_left = copy_from_user(void*to, const void *from, unsigned long n ); bytes_left = copy_to_user(void*to, const void *from, unsigned long n ); |