For this and each of the following programming homework assignments,
you will build a new Linux kernel. The image of the kernel you build
for this assignment should be vmlinuz.hmwk1
. Grading for
this assignment will be done based on vmlinuz.hmwk1
. Note
that the version of Linux running as the Host OS on the CLIC machines of
the Guest OS that you will modify running in VMware.
scp linux-2.6.17.tar.bzip2 root@172.16.85.xxx:Don't forget the colon at the end!
tar xjf linux-2.6.17-13
A little care is needed to make sure the linked files are copied when
written. If you use emacs to edit, you should have no problem: when
you first make a change to a file in emacs, it moves the original file
link to a backup (the ~ file), and makes a copy to work on. Likewise,
the patch
program moves the
original link out of the way and makes a copy before patching a
file. However, if you use vi, you will have to run mv file
file~; cp file~ file before you start editing. Yes, this may
seem like a pain in the neck right now, but you'll be glad for
the time saved, based on an earlier kernel measurement:
$ time cp -a linux-2.6.17-13 linux-2.6.17-realcopy real 0m41.476s user 0m0.140s sys 0m3.340s $ time cp -al linux-2.6.17-13 linux-2.6.17-linkcopy real 0m0.428s user 0m0.030s sys 0m0.400s
Also, comparing files with the original sources is much quicker:
$ time diff -Naur linux-2.6.17-13 linux-2.6.17-realcopy > /tmp/patch1 real 0m2.615s user 0m1.250s sys 0m1.360s $ time diff -Naur linux-2.6.17-13 linux-2.6.17-linkcopy > /tmp/patch2 real 0m0.278s user 0m0.160s sys 0m0.120s
cp ~w4118/vmware/config-2.6.17-13 .
make modules make modules_install
depmod -ae 2.6.17-13
This will set up the modules.dep file in /lib/modules/2.6.17.13/ check to make sure that it's there.
mkinitrd /boot/initrd-2.6.17.img initrd-2.6.17
This will create this file: /boot/initrd-2.6.17.img. Verify that it has been created.
grub
, know where to find
your kernel, edit the file /boot/grub/grub.conf by adding
the following lines:
title HW1 Kernel root (hd0,0) kernel /vmlinuz.hmwk1 ro root=/dev/VolGroup00/LogVol00 initrd /initrd-2.6.17.img
Whatever you put on the line next to title will be displayed as the name of your kernel image the next time you boot.
Do not remove the default kernel. You need a fail safe way to boot your VM in case your own kernel doesn't work.
Write a new system call in Linux. The system call you write should take one argument and return the process state information for that process. Note that you will be adding this system call to the kernel based on the 2.6.17-13 kernel you previously built with kernel debugging installed. The prototype for your system call will be:
int pinfo(struct pinfo *info);
pid_t
is defined in /usr/include/sys/types.h.
You should define struct pinfo
as
struct pinfo { int pid; /* process id */ long state; /* current state of process */ long nice; /* process nice value */ int parent_pid; /* process id of parent */ int children; /* total number of children */ int youngest_child_pid; /* pid of youngest child */ int younger_sibling_pid; /* pid of younger sibling */ int older_sibling_pid; /* pid of older sibling */ unsigned long start_time; /* process start time */ long user_time; /* CPU time spent in user mode */ long sys_time; /* CPU time spent in system mode */ long uid; /* user id of process owner */ char comm[16]; /* name of program executed */ };in /usr/src/linux/include/linux/pinfo.h as part of your solution.
Your system call should return 0 unless an error occurs. Your code should handle errors that can occur but not handle any errors that cannot occur. At a minimum, your system call should detect the following conditions and respond as described:
If the address for the pinfo
structure is null, return -22.
If a value to be set in pinfo
is accessible through a
pointer which is null, set the value in pinfo
to -1. For
example, the youngest_child_pid
should be set to -1 if the
process does not have a child.
The referenced error code is defined as EINVAL in
/usr/src/linux/asm/errno.h.
You should start by reviewing the "How System Calls Work in Linux/i86" section of the Linux Kernel Hacker's (LKH) Guide. Note that because the Linux kernel changed over time, the procedure is no longer exactly correct. However, there are messages appended at the end of the section that will help significantly. Two more recent articles that discuss how to add system calls can be found ?here? and ?here?.
Each system call must be assigned a number. Your system call should be assigned number 222.
You should create a patch to store changes you've made to the kernel and to submit your changes for this problem. If you've changed 50 lines of code, a patch will be about 50 lines long. This is much easier to store, email, or move around than the full 283,000-line source tree. To create a patch, first back up your .config file, then do a make distclean in your changed source tree (you don't want object files, config files, etc. in your patch). Then, from the directory above your source tree (e.g. /vmware1/src), run
diff -ruN linux-2.6.17-13 linux-2.6.17-changed > changed.patch
Notice that the original source tree is first, and the changed second. There is a particular "algebra" to patches. You may find it helpful to think of diff as subtracting two source trees. The result of this subtraction is a patch. To apply a patch, then, is to add this difference back. If you supply the -R option to the patch program, you subtract the difference, instead of adding. If you thus "subtract the difference" from your changed tree, you end up with the original tree. So, to contain all the information of n kernel trees, all you need is the n - 1 patches between them, and only one full kernel tree (and it can be any one of the n).
Linux maintains a list of all processes in a doubly
linked list. Each entry in this list is a task_struct
structure,
which is defined in /usr/src/linux/include/linux/sched.h. In
/usr/src/linux/include/asm/current.h, current is defined to
inline a function which returns the address of the task_struct
of the currently running process. All of the information to be returned in the pinfo
structure can be determined by starting
with current.
In order to learn about system calls, you may also
find it helpful to search the Linux kernel for other system
calls and see how they are defined. The file kernel/sched.c
might give some useful examples of this. The getpid()
and getuid
system calls might be useful starting points. The system call
sys_getpid
defined in /usr/src/linux/kernel/sched.c uses current
and provides a good reference point for defining your system
call.
Your kernel has partially reserved a slot in the
syscall
table for ni_syscall
(when you see it, you will know
what we are talking about). Since ni_syscall
means "not
implemented", do not be afraid to commandeer that slot.
To test your system call, write a simple program that calls
pinfo
. Your program should print all the process state
information for the calling process. Run the program several
times. Which fields in the pinfo
structure change? Which ones do
not? For each field in the pinfo
structure, discuss how
frequently it changes and why as part of your writeup.
Although system calls are generally accessed through a
library (libc), your test program should access your system call
directly. This is accomplished by utilizing the appropriate
_syscall
macro in /usr/include/asm/unistd.h. As explained in
LKH, which _syscall
macro you use is determined by the number of
arguments passed to your system call.
The output of the program should be easy to read, and the program should check for errors such as invalid input or too many or too few arguments. The ps command will provide valuable help in verifying the accuracy of information printed by your program. You can access detailed information on the ps command by entering man ps.
One of cool features of Linux is the ease with which you can implement kernel modules. Kernel modules are pieces of code that can be added to a running kernel; they are often used for the implementation of device-drivers. Write a loadable kernel module that creates the same system call as question 3 of this assignment, this time called pinfo2. This system call should occupy index number 223 of the syscall table. Remember that you should be compiling your module on the host machine, but you will need to specify the correct include path for the version of the kernel that you are using in your Makefile. In order to learn about kernel module programming, you should check out The Kernel Module Programmer's Guide as well as Loadable Kernel Modules. Additional Information:
Remember, as a safety measure, you are strongly encouraged to copy the source files you plan to modify to your home directory on the host system.
printk()
statements in system calls will print their output
to the console whenever they are invoked. To request
printk()
messages be sent to a log file, insert the
following line into the /etc/syslog.conf file:
kern.* /var/kern.log
This will cause printk()
messages to be written to
/var/kern.log. You can send a signal to request the syslog
daemon re-read the updated /etc/syslog.conf file, with the
command kill -1 pid where pid is the process ID of the syslogd process.
A lot of your problems will come from system administration issues. If nobody in your group in familiar with unix, you might want to pick up a book on system administration or consult some of the guides at The Linux Documentation Project.
Some people are confused by the virtual network connection between the host and guest VMs. Usually for all ftping, sshing and telneting, you must specifically identify the target machine's IP address. Usually, the host sees the VM as 172.16.156.128 and the VM sees the host (cityname.clic.cs.columbia.edu) as 172.16.156.1. If these numbers do not work, type /sbin/ifconfig to see what numbers have been assigned (they should be pretty close to these). In cases where more than one VM is running simultaneously, different network numbers might be established.
To add to the previous point, your group should not, under any circumstances, be running two or more instances of your VM simultaneously. This often happens through the use of VNC. View the VM itself as a critical section and use some method (e.g., emailing your fellow group members) to enforce mutual exclusion.
You must submit a single file named your UNI.tar.gz or your UNI.tgz. Any other filename and we will dock points! That tar file should contain one subdirectory named p1 which should contain the kernel patch, the readme file, sample output, and testing programs. Your readme file should describe what changes you made to the kernel, how to run your test programs and what the expected output should be. For each programming part, if your program(s) do not work, you must submit file named nonworking.txt. Inside this file, you need to describe what problems you ran into, where your program fails and what you think are the reasons.
Do not remove old kernels, keep them around because we will need to login to your VM and test them. So your boot loader should have: