Here's where you prove that kernel hacking is NOT magic. Remember, at this point you have compiled and booted your new Linux kernel. Now we're going to "hack the kernel." We're going to add a printk, a function that prints out a message, during bootup.
First, bring up the file init/main.c in your favorite editor. If you are using source control, be sure to check out the file first. In BitKeeper, use the command bk edit init/main.c. In CVS, use cvs co init/main.c. Find the calibrate_delay(void) function in the file.
The first lines are declarations of a few variables, and will look something like this:
unsigned long ticks, loopbit; int lps_precision = LPS_PREC;
Now, type in the following line just below them:
printk("*** I am a kernel hacker! ***\n");
And now, recompile using make bzImage or make zImage, run LILO or ybin or whatever you need, and boot your new kernel. You don't need to run make clean. Watch the screen when you boot up - you should see your new message. If it scrolls off the screen too quickly for you to read it, login and type:
$ dmesg | less
You should see your message near the beginning of the output.
Congratulations! You have just hacked the kernel. printk's are one of the main tools of the kernel hacker. You will need to use them constantly.
More printk Tricks
printk is in many ways the kernel equivalent of the C standard function printf. The formats are mostly the same. You will use certain formats far more often in kernel code than you do in user code. The "%x" or "%p" formats are especially useful. "%x" says to print out the value in hexadecimal, which is base 16, and is usually far more useful information than the value in decimal, which is base 10. This is because the logical unit of information in a computer is a byte (8 bits), and a byte fits into two digits in a hexadecimal representation. So the hexadecimal value:
Represents these 4 bytes (ignoring endian-ness):
0x12 0x34 0x56 0x78
As you can see, it's easy to separate out a hexadecimal value into individual byte values - just read it two digits at a time. The "0x" part just says that the following numbers are in base 16, not base 10. (Note: only obsolete and irrelevant machines have bytes which are not 8 bits. We don't care about bytes which are not 8 bits long.)
The "%p" format says to print out the value as a pointer, or an address in memory. This format will depend on the machine, but is always in hexadecimal. It prints leading 0's, too.
Your next assignment is to print out the address of a variable. Below your first printk, add this printk:
printk("The address of loops_per_jiffy is %p\n", &loops_per_jiffy);
Recompile and boot. Now you know what virtual address in memory the variable loops_per_jiffy is stored at. You can also find this out from the file System.map in your top-level Linux source directory:
$ grep loops_per_jiffy System.map
Next, we're going to learn about loglevels. You can specify each printk to have a certain level of importance. Depending on the current loglevel, some messages will be printed to the console (your video monitor, usually) and some won't. Add this below your other printk's:
printk(KERN_DEBUG "*** This is a debug message only. ***\n");
If your loglevel is configured normally, you won't see this message printed out during bootup. Once you've finished booting, login and look at the kernel messages again:
$ dmesg | less
Below your other messages, you should see:
*** This is a debug message only. ***
This is convenient since you only have to look at the output when you want to, instead of having it printed on the screen (possibly while you're typing). The definition of all the different KERN_* loglevels in include/linux/kernel.h. Usually, you will only need KERN_INFO and KERN_DEBUG.
Whenever you want to find out what's going on inside the kernel, a good way to start is by putting printk's in strategic places. Be careful that you don't print out too much information - it can slow down your kernel to the point of unusability. In certain places, adding a printk can cause a crash, or cause a bug, or fix a bug. To find out exactly what printk does, start reading in the file kernel/printk.c.
Have fun with your printk's!