CS 232
Project 4: Kernel Programming
Overview.
This 2- or 3-person group project
is to learn how system calls are implemented by implementing
a few yourself.
To do this, you will have to modify Linux kernel source files.
The Linux kernel is written in C, not C++, and so you
must do this project in C.
Details.
We have seen that most Unix-variant operating systems are
layered systems, with user-level programs at the
top layer, and the OS kernel at the bottom layer,
just above the host computer's actual hardware.
Our previous assigments have involved writing code that runs
at the user's level -- in user mode.
In this assignment, we will for the first time write code that
runs at the kernel level -- in supervisor mode.
As such, the code you write will have full control over
and access to the kernel, its data structures,
and the machine's hardware, including priveleged instructions.
Because the kernel is the foundation of the operating system,
an error in the kernel's code may render the host computer unuseable.
Since
- this assignment requires you to modify the Linux kernel,
- few of us can write perfect code the first time,
- errors in the kernel can render the machine unuseable, and
- we share our lab with people from other courses,
it would be potentially catastrophic for you to modify the actual
kernel on one of our lab machines.
(Also, we don't want to give out the root password for Linux
on the lab machines.)
Instead, we will use the virtual machine approach we saw
earlier in the course.
More precisely, you will run your own personal copy of Linux on
top of a virtual machine called VMWare,
which is running on top of the copy of Linux
that is running directly on your machine's hardware:
In this "top" copy of Linux, you will have the root password,
and thus be the system administrator,
without "endangering" anyone or anything else in the lab.
(See Gary Draving to get your username, password,
and the root password for your "top" Linux.
For safety's sake, you should change this password immediately.)
To work on this assignment, you must thus run VMWare,
and then boot your own copy of Linux to run on top of VMWare.
To store your own copy of Linux,
we have provided you with what VMWare calls a
virtual disk image
-- a special file that VMWare treats like a disk volume.
Your virtual disk image has been preloaded with a copy of Linux.
By specifying this disk as a boot device for VMWare,
you can boot your own copy of Linux, on top of VMWare,
and login as its system administrator.
You may then modify the kernel of this "top" copy of Linux at will,
without harming the kernel of "bottom" copy on which VMWare is running
(and which is used by the students in other courses).
By the time you read this, your VMWare virtual disk should be
in your home directory.
Since your home directory is available on each machine in the Systems Lab,
your virtual disk will be accessible from any machine in the lab
that Gary has set up to mount the home directories.
You can thus work on this assignment from any of those machines.
You should read
Gary Draving's directions,
taking special note of how long kernel recompilations may take.
Your VMWare virtual disk should contain a full installation of Linux,
plus the C source code for the Linux kernel,
in the /usr/src directory.
By modifying this kernel source code and then recompiling your kernel,
you can change its behavior.
Besides altering the existing kernel source code,
you may also access or alter existing kernel data structures,
create new data structures, write new kernel functions, and so on.
Creating a system call for Ubuntu 2.6.28.10-custom
involves three steps:
-
Define the system call by adding it to an existing file containing
other system calls
(e.g., /usr/src/linux/kernel/sys.c).
Examining the other system calls in this file may prove informative
as to how you should define yours.
Be sure to define it using the asm_linkage directive.
-
Give your system call a number, which will be its index
in the kernel's table of system calls.
This number should be added at the end of the list in
/usr/src/linux/arch/x86/include/asm/unistd_32.h,
and you should follow
the naming convention indicated in that list.
When you do so, you will also be declaring an identifier
(e.g., __NR_printMyName) that can be
used for that number (i.e., a constant).
-
Create the table entry for your system call.
This is done by adding an entry at the appropriate spot in
the system call table whose entries have the form:
.long SYMBOL_NAME(sys_*)
in /usr/src/linux/arch/x86/kernel/syscall_table_32.S.
Again, be sure to follow the naming conventions of the other
entries in this table, and use a descriptive name.
-
Create a "wrapper function" for the system call that cleans up its
interface, and store this wrapper in a publicly accessible directory.
(While it is not generally a good idea to do so, for this assignment
feel free to "cheat" by storing the definition of your wrapper function
in a .h file in your /usr/include directory.)
Your wrapper function should use the syscall() system call
(see its description in section 2 of the online manual)
to invoke your system call via the identifier you gave its number
in #2 above, and pass it any parameters it requires.
-
Recompile your kernel and then reboot it so that your modifications
take effect.
-
Create a program that tests your system call by #include-ing
the file containing your wrapper function and then invoking the
wrapper.
Run this program to test your system call.
Your assignment is to use this procedure (at least) three times to
create three new system calls and "wrapper" functions:
-
one that prints your name to the screen;
-
one that fills a character array (C does not have strings)
with your name; and
-
one that fills a character array with the name of the
user who is the owner of the currently executing process.
(The identity of a process' owner is stored in its process control block,
which in Linux is called a task_struct.
This data structure is defined in the kernel source file
/usr/src/linux/include/linux/sched.h,
and the Linux kernel contains a global point variable current
that contains the address of the task_struct of the
currently executing process.)
Plan of Action.
In the steps below, all paths assume a starting point of
/usr/src/linux/.
-
Read over Gary Draving's directions,
especially the steps for recompiling the Linux kernel;
then work through all of the steps in those dirctions.
-
Start VMWare again.
Make some trivial change to the kernel whose effect you will notice
(e.g., add a printk() statement to
the do_fork() function in
/usr/src/linux/kernel/fork.c,
so that a message is displayed each time the function gets called).
Recompile your modified Linux kernel source code;
then reboot your modified kernel
to verify that your modified kernel works as expected.
-
Using the steps given above,
create your first (simple) system call and wrapper that uses the kernel's
logging function printk() to print your name to the screen,
plus a program to test it.
Recompile your kernel, reboot VMWare, and run your program to test it.
Do not continue to the next steps until you have this working correctly.
-
Using the steps given above,
create a system call and wrapper that receives
a character array buf and its size n from the caller,
and then copies up to n letters of your name into buf.
Write a simple test program that declares a character array and an integer
variable containing its size, passes these values to the system call,
and then displays the contents of the array to verify correctness.
Recompile your kernel; then reboot VMWare and run your program
to verify that your system call works as expected.
As before, do not continue to the next step until
this is working correctly.
-
Using the steps given above,
create a system call and wrapper that receives
a character array buf and its size n from the caller,
and then copies into buf up to n letters
of the name of the owner of the currently executing process.
Write a simple test program that declares a character array and an integer
variable containing its size, passes these values to the wrapper,
and then displays the contents of the array to verify correctness.
You may use the same program to test all three of your system calls.
Note that kernel recompilations are relatively time-consuming,
as are reboots, so you want to minimize your recompilations.
One way to do this is to invest time in a quality design
so that you can focus on coding an already correct algorithm.
If you try to use the compiler to debug a poorly designed algorithm,
you are unlikely to complete this assignment in the allotted time.
Another way is to design and code in pairs.
When it comes to avoiding and/or finding errors
(logic or syntax), two heads may be much better than one.
Normal debuggers run in user-mode; so they are essentially useless
when it comes to debugging at the kernel level.
Debugging a logic error thus consists of inserting output statements
(printk()) into your source, recompiling, and tracing
the output.
Given the time required to recompile, it is essential that you
minimize recompilation; so you should make every effort to ensure
that your logic is correct ahead of time.
Another way to save (a little) time is to write a shell script
to perform the copy operation used to install your new kernel following
each compilation.
If you get stuck, come and see me for some suggestions.
As in any project, you are to use descriptive identifiers,
and in-line comments that explain any 'tricky' parts.
You should document the changes you've made at the beginning
of any file you modify, including the author, date,
and purpose of the modification.
Turn in:
The project grade sheet
(you need to fill out the top part),
attached to a hard copy of a script file, in which you
- Use cat to display each kernel source file
you created or modified
(to save paper, you may delete parts of large source files,
provided you leave the context surrounding your modifications);
- Use touch to change the modification date on each file
you modified;
- Use make to successfully compile your kernel;
- Install your new kernel;
- Boot VMWare using your new kernel;
- Use cat to display your test program(s); and
- Demonstrate that your new system calls work correctly
by running your test program(s).
Please highlight your modifications on the hard copy
of the script file, so that the grader can find them easily.
Due date: Wed, Apr 18, at 11:59 pm.
Warning: This is the most time-consuming project of the semester.
You will likely need the entire time to get it working
correctly.
Begin work on it immediately!
Calvin >
CS >
232 >
Projects >
4
This page maintained by
Joel Adams.