Assignment 2 Tips
I know that this assignment may look a bit intimidating, particularly once you start wading through the Linux kernel source code for UML, but it is actually doable with what you know already from CPSC 457.
The most important pieces of general advice are:
- Start early! Don't expect to be able to do this assignment in the last day or two before the deadline. It is difficult, and it involves reading and understanding a lot of kernel code. Start early, so that you can familiarize yourself with the workings of UML. You will probably need a couple hours in the lab just playing around with UML before you fully understand what the assignment is all about.
- Think things through. The better your mental model of UML and the Linux kernel, the easier it will be for doing your kernel programming. Kernel programming is much harder than application programming, and the consequences of buggy programming are even more dire. Don't just write code willy nilly; think it through carefully as to what code needs to go where, and why. Also realize that using printf() statements for debugging isn't really possible. While there is printk() at your disposal, using it judiciously is important, because getting 1000 messages on your screen doesn't always help!
- Try things out. User Mode Linux (UML) is meant to be a safe "sandbox" environment for you to experiment with things and see what works, what breaks, and how it breaks. And "hands on" learning is often the most valuable kind. Try things out, see what happens, and try to explain it to yourself as to why it does what it does. It should be easy to crash UML as much as you want, but crashing the physical machine will be hard to do. Don't be shy!
- Write it down. If you find something that works, write it down, so that you can recreate or redo it again later. If you find something that doesn't work, write it down, so that you don't make that same mistake again. If you find that mysterious file or system call that you have been looking for, write it down so that you know where it is. Your own notes can be your most valuable time-saving resource!
- Save your work. Bear in mind that much of the code that you modify is in a /tmp directory on a machine in the lab. If the machine is ever rebooted, your code is gone. If you have just spent 2 hours debugging your sched.c, you probably want to save a copy in your real home directory. If you switch machines, you are starting over. If you ever need a clean new copy of the kernel, then doing 457-init gets you back to a fresh state.
Enough of the high-level motherhood principles. How about some real tips?
Here are some from my own weekend spent in the lab, working with UML and Linux 2.6.24.
To get a brand new clean version of UML, run 457-init:
/usr/uml/457-init
To start up UML, do the following:
cd /tmp/YOURNAME/uml
./startuml
To login as root in UML, simply type "root" to the login prompt on one of the UML windows. (There is no password required for root.)
To make your existing home directory of student files accessible to you
from within UML, use the mount command like this:
mkdir /host
mount none /host -t hostfs -o YOURHOMEDIRECTORY
cd /host
cd ONEOFYOURDIRECTORIES
To create a new user named "fred" within UML, do the following:
useradd fred
This should create a new entry in the /etc/passwd file,
along with an assigned user id number, and a new home directory in /home.
As superuser, you can edit the password file to make the login password null (empty),
so that you can login to a UML window as fred with no password.
Or you can have fred use the passwd command to set a desired password.
You can use the "su" command to switch back and forth between your
different user identities in UML (root or fred),
or you can use one of the login windows for each.
You can now create and edit files as fred if you wish, or you as superuser
can copy some files to fred's directory using:
cp YOURDIRECTORY/looper.c /home/fred/
chown fred /home/fred/looper.c
Fred should now be able to compile and run the looper program.
You can use ps to check the status of the executing process,
either as fred or as superuser.
To shut down your current UML session, from your root window run:
shutdown -h now
This should gracefully terminate UML and close all active windows.
If you are ready to modify some kernel code,
then the following will be useful for you.
(Note that this is done from outside the UML environment.)
cd /tmp/YOURNAME/uml/linux/kernel
cp sched.c schedsave.c
emacs sched.c
cd ..
make linux ARCH=um CC=gcc CXX=g++
Watch for any kernel compilation errors. Fix any that arise.
If the kernel compiles successfully, you should be able to:
cd ..
./startuml
and run your new Linux kernel!
If you want to be sure the kernel is running your code, you can put some printk() statements in selected places. (Example: printk("Yo dude!"); ) By default, printk() messages will appear on the console, which is usually the first (root) of the four login windows that UML creates. You can also use the dmesg command to view the message log. It is best to keep your printk() messages brief, as there will probably be a lot more of them than you expect!
If you are trying to modify kernel system calls,
then these are some useful files to know about:
/tmp/YOURNAME/uml/linux/include/asm/arch/unistd_32.h
/tmp/YOURNAME/uml/linux/arch/x86/kernel/syscall_table_32.S
/tmp/YOURNAME/uml/linux/arch/um/kernel/syscall.c
/tmp/YOURNAME/uml/linux/arch/um/include/syscall.h
For example, you should be able to install a "Hello world!"
system call like the TAs described in the tutorials.
If you are trying to modify the CPU scheduler itself,
then these are some useful files to know about:
/tmp/YOURNAME/uml/linux/include/linux/sched.h (see line 917)
/tmp/YOURNAME/uml/linux/kernel/sched.c (see line 3617)
/tmp/YOURNAME/uml/linux/kernel/sched_fair.c
/tmp/YOURNAME/uml/linux/arch/x86/kernel/process_32.c
/tmp/YOURNAME/uml/linux/kernel/fork.c
One good starting point is to have the schedule() function in sched.c print out a message whenever it sees a process owned by fred. Set this up, boot the kernel, and see what happens when fred logs in.
Another fun trick is to change the default scheduling priority for processes owned by fred. One possible place to do this is in the do_fork() routine in fork.c. Set this up, boot the kernel, and use ps to see the status of processes launched by fred. Try lower values and higher values to see what happens.
Here are some updated tips for those who need it.
Unix and Linux systems have a "nice" command that allows a user to voluntarily designate a lower priority for a process that they are submitting for execution. For example, "sleep 60" runs at normal priority, while "nice sleep 60" runs at slightly worsened priority. Do a "man nice" to learn more about nice.
Then take a very careful look at how the nice command is implemented in the kernel. You basically want to do the same to implement your own command (evil?), which has three differences from nice. First, it is the evil superuser who is adjusting the niceness level for the user, rather than the user themself. Second, you can adjust niceness to any arbitrary value (upward or downwards). Third, you can set this up for any FUTURE processes created by that user, rather than just a single currently running process.
Try having the SAME user (e.g., fred) run MULTIPLE processes concurrently with different nice levels. If one of the running processes has pid 12345, then do a "cat /proc/12345/sched" to see detailed information about the process (e.g., priority level, running time, scheduling load weight). See if it has the values you expected. Look at the "prio_to_weight[]" table in the kernel to see if things make sense to you.
Try having DIFFERENT users (e.g., ari and carey) run processes concurrently with different nice levels. If one of the running processes has pid 12345, then do a "cat /proc/12345/sched" to see detailed information about the process (e.g., priority level, running time, scheduling load weight). See if it has the values you expected.
If things are still not working right at this point, then it is probably the CFS (Completely Fair Scheduler) CPU scheduler in Linux that is messing you up, by being completely fair to all user processes. Look for a way to disable this scheduler (Hint: see the autoconf.h file). Note that you will have to COMPLETELY recompile your kernel to have this change take effect.