Tools

This section aims to explain the development tools that are fundamental to the kernel development process.

Editors

There is a wide choice when it comes to choosing an editor. Many heated debates have taken place over which editor(s) is best suited to (kernel) programming. In order to appease both sides and to give a balanced view, both vim and emacs will be presented here! The idea is help the undecided programmer figure out which editor best suits his or her needs.

vim

Just about every UNIX-like system has at least vi available, if not vim (vi-improved) which has some extra, tasty features like syntax highlighting.

Reasons to use vim for kernel programming:

  • Integrates really nicely with ctags. If you make tags in the top level kernel source directory, you can then quickly access function & variable definitions by pressing Ctrl-] while the cursor is over the function or variable in question.

  • Auto-completion of variable and function names can save edit-compile cycles by reducing spelling mistakes. This particularly applies to kernel programming as some of the function names are quite long and involved. To use this feature, type the first part of the variable or function and press Ctrl-P repeatedly to search backwards through the document for possible matches.

  • Fast loading time.

  • Small memory footprint; leaves more memory for kernel recompiles.

  • Highly configurable. Amongst other things, command keystrokes are easily re-mapped; useful if you find a common command awkward or uncomfortable.

  • As of version 6.0, integrates with cscope as well. (Thanks to Bohdan Vlasyuk for this addition)

Other reasons to use vim:

  • If you can use vim, you will always have an editor to hand. Even the most basic rescue boot disks have at least vi. In short, one day you'll need to know how to use it, be it sooner or later! The more proficient you are, the less painful that experience will be.

  • The keyboard shortcuts involve less hand movement, for an unmodified key map at least.

Reasons not to use vim

  • Constant use of the (poorly positioned) Esc key reduces typing efficiency. Possible solutions involve i) pressing Ctrl-[ instead ii) re-mapping a more ergonomic key to be Esc.

  • vim is linked against a lot of libraries: (ldd /usr/bin/vim to see the list) including some X libraries. If this is a real concern, you may prefer vi.

  • Some people really don't get on with the modal way of doing things.

Probably the best way to learn the basics of vim is to follow the tutorial; start vimtutor and enjoy. The vim-HOWTO also has a lot of useful information.

For additional information on editing C source code with vim, see the C-editing-with-Vim-HOWTO

For more general information on vim see the Vim Homepage

emacs

Most of the information in this section is also applicable to xemacs.

Like vi, emacs also has a long heritage and a large number of loyal users.

Reasons to use emacs for kernel programming:

  • Large library of emacs lisp programs available to aid productivity.

Other reasons to use emacs

  • Good integration with etags.

  • Also integrates with grep and cscope.

  • gdb integration.

  • The keystrokes may seem more intuitive (e.g. you can type into a document immediately without having to enter a mode), and therefore maybe preferred by those who don't get on with the modal way of doing things.

Reasons not to use emacs

  • Constant use of the Ctrl key makes RSI a real concern. Re-mapping Caps-Lock to be another Ctrl key is recommended.

  • It is a big package so may not be suitable for systems with limited hard disk space.

  • Longer loading time on modest systems.

  • Larger memory footprint, so less memory available for kernel recompiles.

  • etags is considered unstable by some, dumping core frequently.

One of the best ways to learn emacs is to work through the tutorial which can be accessed by loading emacs and issuing these keystrokes: Ctrl-h then t.

If you like the way that emacs works but don't like it's size, you could try jove which is a minimal clone. The source can be downloaded from the jove FTP site.

For more information on emacs see the emacs home page.

In summary, if you like to do everything in one place (write code, debug code, review patches, email, news groups), emacs is probably for you. If you like smaller, more application specific programs, a smaller editor (like vim) may suit you better.

Development

make

make is a program that is used to determine which parts of a multi-source-file project need to be rebuilt. Only those source files which have been modified are rebuilt before the final linking step. This is a big plus for the kernel programmer as once the "stock" kernel has been built, recompiles to include changes take seconds instead of minutes. make operates on a Makefile, a file containing the relationships and dependencies between source files along with the commands used to build them.

Just about every directory in the kernel source tree has a makefile, including the top level directory. When you invoke make dep bzImage modules, make operates on the top level makefile which recursively works through the makefiles in each of the subdirectories (depending on which options were included during the config step).

lclint

Some programming errors can be caught early on in the development cycle, saving time and effort. You may not have bothered with formal code analysis before, but now would be a good time to start; bugs in kernel code are generally harder to track down and can have more serious implications than their user-space counterparts. Generally speaking, a higher standard of programming is called for; while a user may be prepared to put up with an email client that occasionally core dumps, they certainly won't put up with a kernel that freezes or worse, causes data loss.

lclint is a program that can be used to statically check C code, that is to check it before it is even compiled or executed. Like lint, lclint can be run on unmodified C source code and used to catch classic programming mistakes. However, lclint can do a whole lot more for you, but you must give it "clues" by annotating your source code using comments in the appropriate way.

In short, lclint can give you some of the foresight that an experienced programmer has at their disposal. While it may not be perfectly suited to kernel C code (which has some unusual attributes), it can still be used to prove fragments of code prior to inclusion in the kernel source.

More information on lclint can be found at the lclint home page.

Source Code Navigation

One of the most daunting activities that faces a new kernel programmer is navigating a very large volume of unfamiliar source code. This section describes some invaluable tools to help.

grep

grep is used to find lines matching a given pattern; a useful tool for quickly locating variable or function definitions and use. Example: Suppose you want to find all instances of task_struct:

$grep -r task_struct * | less

This command recursively (-r) scans all files in the source tree and prints lines containing task_struct, those lines are piped to less, which provides a scroll-able output. man grep for more details!

lxr

lxr enables you to navigate source code via a web browser, where all variables and functions are links to their respective definitions. Probably the best way to read and understand code.

You can browse the kernel source code online at the Linux Cross Reference Page or at innominate.org, or download lxr and set it up on your box, useful if you don't have a permanent Internet connection. The setup process is quite involved but details are given in the README file that comes with the lxr source code.

cscope

cscope is useful for doing many of the things you could use grep for, but is more intelligent and provides a nicer interface to work with. You can search for definitions, uses, strings etc.

Before you can use cscope you need to build an index file. This can be done by issuing the command cscope -b -R -k in the top level source directory; -b to build the index, -R to search recursively through the source tree and -k to indicate kernel use; this ensures the appropriate include files are used when generating the index.

To start a cscope session, type cscope -d. You will then get something that looks like this:

Cscope version 15.3                                    Press the ? key for help













Find this C symbol:
Find this global definition:
Find functions called by this function:
Find functions calling this function:
Find this text string:
Change this text string:
Find this egrep pattern:
Find this file:
Find files #including this file:

The top half of the screen is used to display search results, while the lower half is used to issue commands. Example; suppose you want to find the definition of the file_system_type data structure. Use the arrow keys to move the cursor to the Find this global definition: field and enter the name of the data structure, followed by enter. If all is well, cscope will find just one instance and will open your favourite editor (set by the EDITOR environment variable) to display the appropriate section of the file.

Now suppose you want to find the definition of super_block. Follow the procedure above, which should give you this output:

Global definition: super_block

  File          Line
0 vxfs_extern.h  44 struct super_block;
1 super.c       265 int (*test)(struct super_block *, struct buffer_head *);
2 udfdecl.h      52 struct super_block;
3 fs.h          688 struct super_block 
4 fs.h          936 struct super_block *(*read_super) (struct super_block *,
                    void *, int );
5 udf_fs_sb.h    71 __u32 (*s_partition_func)(struct super_block *, __u32,
                    __u16, __u32);



Find this C symbol:
Find this global definition:
Find functions called by this function:
Find functions calling this function:
Find this text string:
Change this text string:
Find this egrep pattern:
Find this file:
Find files #including this file:

This time, cscope has found multiple possible definitions. You can view each definition in its context by pressing the numbers given next to the list of files, alternatively, you can use Tab to move to the top half of the screen and the arrow keys to select a definition. Simply quit out of the editor to return to cscope. Press Tab again to return to the command area. Note that you can also move up and down using Ctrl-p and Ctrl-n, which saves moving your hands away from a typing position. To exit cscope, press Ctrl-d.

cscope source code and documentation is available from the cscope home page, I would advise using version 15.3 or later as this version includes support for recursive indexing and using kernel headers.

ctags

ctags provides similar functionality to cscope, but integrates closely with your editor, allowing look-ups with a few key strokes. Like cscope, ctags builds an index file to speed up searches. The easiest way to generate the index is to type make tags in the top level kernel source directory.

When using vim, moving the cursor to a use of a function/variable/data structure/type and pressing Ctrl-] should take you to the definition. Ctrl-t takes you back to where you were. Note that lookups can be nested, in which case, Ctrl-t takes you up one level.

Details for using ctags with emacs are welcome!

ctags can be obtained from the ctags home page. Debian users will want to install the exuberant-ctags package.

Summary

A brief summary of the tools presented:

  • lxr is useful for browsing and understanding code, as opposed to editing.

  • cscope is useful for finding a definition if you know the name of function/variable/data structure concerned and want quick access to the source file, perhaps to add a field to a data structure.

  • ctags is useful for doing quick look-ups during an editing session, but isn't quite as smart as cscope is.

Source Code Manipulation

diff

diff is used to compare two files and output any differences between them. When used in unified mode (-u option) to compare two files (original and modified) a "patch" is produced:

diff -u linux-2.4.14/drivers/char/keyboard.c linux/drivers/char/keyboard.c > my_keyboard_patch

Where the linux-2.4.14 directory holds the original, unmodified source tree and linux directory holds the one you have hacked around with. Distribution of ideas and modifications as patches is a lot more convenient and efficient than distributing complete files or source trees.

The procedure above can be used when just one file has been modified, but what if you need to modify a lot files and produce a patch?

diff -urN linux-2.4.14 linux > my_hefty_kernel_patch

Note that the convention is to generate patches from the directory above the top level kernel source directory i.e. /usr/src if you keep your modified kernel source in /usr/src/linux.

Note that if you are generating a patch to post to the Linux Kernel Mailing List, be sure to follow the instructions given in the FAQ exactly. The FAQ can be found at: http://www.tux.org/lkml/

Of course, use of diff is not restricted to generating patches; it is a useful tool for finding out what has changed between two kernel releases. Uncompressed patches are human readable to some extent; the format is fairly self explanatory. You can even grep a patch if you know what you are looking for.

patch

patch is used to apply patches to a file or a source tree:

cd linux-2.4.13patch -p1 patch-2.4.14

This procedure would update a 2.4.13 tree to 2.4.14. The -p1 option strips the top level directory off all filenames within the patch (as you are patching from inside the top level source directory). Of course, you could apply the patch from the directory above, but you would need to have your directories named in the same way as when the patch was generated. Note that patches are often distributed in compressed form, to save bandwidth. You can save disk space (and typing) by uncompressing patches as you apply them:

bzip2 -dc /usr/src/patch-2.4.14.bz2 | patch -p1

Simply replace bzip2 with gzip if the patch was gzipped instead of bzipped.

A useful script is included in the kernel source tree to semi-automate the process of upgrading a source tree by applying successive patches: linux/scripts/patch-kernel. Read the script to see what it does and how to use it, instructions are given in the comments at the top of the file.

It is often a good idea to do a "dry run" of applying a patch, especially if you are patching a heavily modified tree, or are attempting to apply an old patch against a newer tree. Backing out a partially applied patch can be time consuming and generally is not much fun!

bzip2 -dc /usr/src/patch-2.4.14.bz2 | patch -p1 --dry-run

What if the patch does not apply cleanly? If only a couple of files failed, you could apply the patch anyway and sort things out with a text editor afterwards, alternatively you could manually go through and apply the patch by reading it and making the changes by hand. The manual approach is sometimes necessary to resolve conflicting patches and is a useful technique if you want to understand exactly what a patch changes.

What if you want to remove a previously applied patch? Easy:

bzip2 -dc /usr/src/patch-2.4.14.bz2 | patch -R -p1

This would take your source tree back to 2.4.13.

RCS

Any development work is an incremental process, particularly so at the debugging stage. It always pays to keep a record of the process using some form of revision control system, so that a bad change can backed out with minimal effort. A very basic revision control system can be implemented by just keeping a backup of a file before making major changes, however this approach tends to become cumbersome very quickly.

Enter RCS, the little brother of CVS. CVS is great for large projects with many contributors, but is overkill for small personal projects. One of the attractions of RCS is it's simplicity.

First create the RCS directory in the same directory as your source files:

mkdir RCS

Then "check in" a source file:

ci -u some-file.c

You will then be prompted to give a description. By default, RCS deletes the working file upon check in, so you will want the -u option which automatically checks the file out again. Check in all the files you are working on in this way.

Make some changes to one of your source files and check it in again. You'll be prompted for a summary of the changes and the version number will be incremented.

Suppose you have made a mess of the working file and want to revert to a known good version (1.7), check it out using this command:

co -l -r1.7 some-file.c

The -l flag locks the file and gives you write access to it (otherwise you get read access only).

RCS stores the initial file and only differences between versions, saving disk space. For more information on RCS, see the following man pages: rcs, ci, co, rcsintro, rcsdiff, rcsclean, rcsmerge, rlog, rcsfile and ident.

CVS

While RCS is good for basic projects involving (changes to) a limited number of files, it quickly becomes unwieldy for larger projects, particularly so with contributions from more than one developer. CVS is particularly good in this area.

CVS Commands

All cvs commands are specified as sub-commands of the cvs command. Both the cvs command and the sub-command can be passed options, but their position in the command line should be as shown here.

$ cvs [cvs options] sub-command [sub-command options]

  • add: This command will add a new file to source control. You must check in the file using cvs ci after this command to make the change take effect.

  • ci or commit: This command will check in the changes that you have made to the local copy of a file to make them permanent. After this command, anyone updating or checking out the file from the repository will see the changes that you have made. You can either specify the -m "message" option to provide a message describing the change, or this command will execute the editor for you to enter the message.

    When you check in your changes and the local copy of the files are not up to date, cvs will print a message informing you of this. In this case, you have to update the files and then check in your changes.

    To check in the changes in all the modified files in the current working directory, execute the following command from the top of the working directory:

    $ cvs -q ci -m "blah blah ..."

  • co or checkout: This command will make a local copy of all the files in a module. First, it will create a subdirectory with the same name as the module, then it will copy the files into that subdirectory.

  • diff: This command will print the differences between the local copy of a file and the version in the repository.

    To generate a patch for the changes in the current working directory, execute the following command from the top of the working directory.

    $ cvs -q diff -u

  • history: This command will print information about the cvs repository. By default, this command only prints information that pertains to you. You can use the -a option to print information about all users. The following are some useful history options.

    $ cvs history -a -o # Show checked out files$ cvs history -a -T # Show all tags$ cvs history -a -e # Show all information

  • import This command will put an existing project into the repository.

    $ cvs import -m "blah blah ..." directory vendorTag releaseTag

  • log: This command will print the messages that were specified when each file was checked in.

  • remove: This command will remove a file from source control. You must check in the file using cvs ci after this command to make the change take effect???

    This command will remove a file from the current branch of the source tree. If someone checks out the module, they will not get a copy of this file. However, if they check out an older version of the module, they will still get the appropriate version of the file.

  • tag: To tag all the files in the current working directory, execute the following command from the top of the working directory.

    $ cvs -q tag tagName

  • update: This command will update the local copy of the files in a module. For any file that is updated, its name is printed preceded by a "U". It will also print the names of the files that you have modified (i.e., files that are different from the copy in the repository) and precede them with an "M".

    If there is a conflict between a file that needs to be updated and the local changes that you have made, the file name will be printed and it will be preceded with a "C". The file will be replaced and the conflicting code will be marked with "" and ">>>>", and the original file will be saved as .#file.version.

    You can always delete your local copy of a file and use the cvs update command to restore the up-to-date copy of the file from the repository. In this case, CVS will print a message that the file was lost and replace the file. The file name will be printed preceded by a "U".

    To list the files that have been modified in the current working directory, execute the following command from the top of the working directory.

    $ cvs -q update

Repository Specification

The cvs repository is specified using the -d option to the cvs command or the CVSROOT environment variable. For example,

$ cvs -d pathName login

Or,

$ export CVSROOT=pathName$ cvs login

If the repository is on the local system, the option is simply the path to the repository. For example,

$ export CVSROOT=/usr/local/cvs

However, if the repository is on another system, the repository specification must also include the access method and the user name and server name where the repository is located. Each of these items; the access method, user name and server name, and the path name of the repository on the server; must be separated by a colon. The user name and the server name are connected with the at-sign ("@").

$ export CVSROOT=:accessMethod:userName@serverName:pathName

Linux Top of Tree (2.4)

To get the Linux source code for Linux 2.4, use the following command. This repository is a mirror that is updated every 30 minutes.

$ export CVSROOT=:pserver:[email protected]:/vger$ cvs loginPassword: cvs$ cd some-directory # For example, /usr/local/src$ cvs -z3 co linux

To update the source code, use the following commands.

$ cd some-directory/linux$ cvs -z3 update -d

Linux Branch (2.2)

Or, to get the Linux 2.2 source code, use the following command

$ export CVSROOT=:pserver:[email protected]:/vger$ cvs loginPassword: cvs$ cd some-directory # For example, /usr/local/src$ cvs -z3 co -r linux_2_2 linux

Your Turn

Make sure you have installed all the tools mentioned in this chapter, practice using them and try to learn the most common commands and keystrokes. Check out the appropriate man and info pages for more information.