This assignment is based on the "System Calls" project in Chapter 5 of the handout selected from Gary Nutt, Linux Kernel Projects.
You may do the work on your own Linux box or on Emulab. However, if you use your own box and it's not running the 2.4 kernel, you might find that the documentation here differs from how your kernel works. That's your problem, not mine.
Nutt's suggested project (adding a get-time system call) is boring.
You already did a get-time /proc
entry in the previous
assignment. Besides, there is already gettimeofday
.
Finally, returning the time is not particularly instructive.
Instead, you will add a new system call that does something (very)
mildly useful. This system call will return you a bit mask telling
which file descriptors are open. The call will accept two parameters:
an in parameter giving the maximum number of file
descriptors to scan, and an out parameter that is a
pointer to an area that will receive the bit masks. If your system
call is named getfds
, the prototype would be:
int getfds(int maxFds, unsigned long* result);and you would use it something like this:
unsigned long result[5]; if (getfds(5 * 32, &result[0]) == -1) { /* handle error */ } else { /* result has bit mask of open fds */ }
You can follow the general outline in the handout for creating your
system call. The code for the call should go in
kernel/sys.c
. You will also need to modify
arch/i386/kernel/entry.S
to add your system call to the table.
I inserted it just after the #endif
s that follow
sys_tux
, which gives it the number 223 under our kernel
configuration.
The system-call code in sys.c
needs to remember that the
second parameter is in user space, not kernel space. Thus, you can't
just store into result[i]
. Instead, you must use
put_user
:
int retval; // ... retval = put_user(something_useful, &result[i]); if (retval != 0) return retval; /* put_user failed */Your function should check
maxFds
and return
-EINVAL
if it is negative. Otherwise, it should return 0
to indicate success.
(Note: there are also functions such as copy_to_user
and
strncpy_from_user
, but you won't need them. It is
interesting to search sys.c
for the string
"user
" to find all the different ways the kernel can
communicate with user-mode processes.)
You can find out whether a particular file descriptor
i
is open by examining the table entry
current->files->fd[i]
. If the value is non-NULL,
the file is open.
If you're not familiar with bit masks, you can set bit i
in a 32-bit word with the following code:
int val = 0; ... val |= 1 << i;Note that you will have to write code that allows for the fact that a single word is only 32 bits, but
i
might be greater than
32. This restriction is the reason why result
is an array.
The handout talks about modifying include/asm/unistd.h
to
add your system call number. That's sensible for "real" calls, but
for this project it's just as reasonable to #define
__NR_getfds
as 223 directly in your test program.
Similarly, stubs normally get put into the C library as separate
files, but you might as well define your stub in the test program.
Incidentally, the handout doesn't make it clear that the function name
mentioned in _syscall2
(foo
in the example)
must match the __NR_xxx
symbol. Call it
getfds
and all will be well.
Your test program should call the getfds
function twice,
each time asking for information about 64 fds. You should check for
errors and print an appropriate message if the call fails. After each
call, the results should be printed on one line in hex, like this:
3fadcbe 7f(or whatever -- the above values are definitely wrong). Between the two calls, the program should use
open
,
dup
, or dup2
to create 35 new file
descriptors.
To build a kernel on Emulab, you must first have a private source tree
that you can work in. Unfortunately, there isn't enough disk space
for us all to have our own trees. Therefore, I have set up a system
that allows you to symlink large parts of the kernel that don't need
to be privatized. Run the command ~geoff/maketree
to
create your own source tree in ~/usr/src/linux
. (Note
that the script takes a while to run.) The tree
will be constructed so that you can change the files
kernel/sys.c
and arch/i386/kernel/entry.S
, which
should be the only files you need to modify.
If you screw up and want to start over, run
~geoff/zaptree
. Note that this program will destroy your
modified source files, so back them up first.
Once you have made a source tree, you can build a kernel by creating
an experiment, cd'ing to ~/usr/src/linux
, and typing
"make bzImage
". Note that you can only do builds
from one of the PCs. The first time you do this, it will take a
while, so I suggest that you start a kernel make immediately after you
create the tree. Once you've made a kernel, subsequent builds will be
reasonably fast.
To run your kernel, you must install it in /boot
. The
usual procedure (which you won't have to use) is:
/boot/vmlinuz
or
/boot/vmlinuz-2.4.2-2
) under a backup name such
as /boot/vmlinuz.old
.
arch/i386/boot/bzImage
into /boot
under a name such as /boot/vmlinuz
or
/boot/vmlinuz.new
.
/etc/lilo.conf
, to tell it about the new kernel.
(Typically, you only need to do this once. On my own
machines, I have an entry for vmlinuz.new
, which
can be booted by hand under the name devel
, and
an entry for vmlinuz.old
, which can be booted
under the name backup
. When I start a new
project, I back up my running kernel to
vmlinuz.old
in case I screw up so badly that the
kernel won't boot. When I am sure that my kernel change works,
I rename vmlinuz.new
to vmlinuz
so
that it becomes the default.)
lilo
to tell the boot mechanism
about the new kernel. This step is critical; otherwise
you'll boot the old kernel even though the new one is in
/boot/vmlinuz
.
Since the above procedure is complicated, and since Emulab makes our lives easy (we can seriously screw up a machine by accident and they can recover it automatically), we have a much simpler procedure:
sudo ~geoff/newkernel
to install your new
kernel.
sudo reboot
to reboot the machine (this will
take a while, but eventually you'll get a login prompt).
If you decide your kernel is completely messed up, you can go back to the original kernel with the following steps:
sudo ~geoff/oldkernel
to revert to the
known-good kernel.
sudo reboot
to reboot the machine.
Both of the above procedures must be run from the console,
which is reached with tip
from
users.emulab.net
. If you try them from an ssh
connection, your connection will be closed as part of the reboot
process.
After the reboot, you can double-check that you're running your own
kernel by typing cat /proc/version
. The kernel version
string will include the date and time when you built your kernel, as
well as your own login name.
When you connect to your test computer with tip
, you may
find that your screen goes into "reverse video" where the foreground
and background colors are exchanged. You can cure this problem by
running ~geoff/unblack
.
Please use cs134submit
to submit the following
files:
Makefile
for your test program, if you have one,
kernel/sys.c
,
arch/i386/kernel/entry.S
,
and
README
file containing a copy of the output
from running your program and a brief explanation of why those
particular values were printed.
scp
to download your source files onto
Turing before you can submit them.
There were two class sessions related to this project. You should have gotten handouts there. However, to save myself from requests from people who should know better, here are links to the handouts:
procfs
Guide.