Lecture 32

Customizing the Kernel

In preparing your machines for class we had to customize your kernels. In fact, we had to compile a kernel module (and just for good measure we complied a complete kernel) to get your machines to work the way we wanted to get them to work. To understand what we did, let's discuss the ways in which kernels can be customized.

There are two popular ways to write an Operating System kernel nowadays:

  1. The microkernel approach
    in which a very simple abstraction layer is imposed on the hardware, supporting a set of primitive system calls for minimal OS services such as management of threads, address spaces, and interprocess communication. This minimal system is known as the microkernel. Then the whole operating system is implemented using these underlying primitives. The idea is that the basic services are separated from the higher level policies.

    This is the approach used in Minix and Mac OSX, for example.

  2. The monolithich kernel approach
    in which a single big program comprises the operating system.

    This is the approach taken in Linux.

The microkernel/monolithic kernel is not the whole story, however, because Linux supports dynamically loadable kernel modules. That is, you can compile a kernel module separately from the kernel, then load it at run time, effectively adding new kernel services. These modules must know about the structure of the kernel, though. So rather than getting access through some kernel API (as in a microkernel), these modules have direct access to kernel data structures. In fact, well written modules will use kernel system calls as often as possible, to the exclusion of directly manipulating kernel data structures, so the difference becomes less clear as the programming gets better.

Ways to Configure a Linux Kernel

The fundamental ways of configuring Linux kernel are the following:
Modifying Configuration Parameters Dynamicall
Typically these parameters are modified by changing files in the /proc file system. An example given by Nemeth is
# echo 32768 > /proc/sys/fs/file-max
As we've mentioned before, the /proc file system does not actually appear on a disk device. Instead, it represents the state of a number of processes. Each process maintaining a file in proc responds to the kernel file system operations: read, write, open, close, etc. The processes present a view of the running operating system as if it is a file. Some of the files are read-only (they give information about the system). Some of the files can be written, allowing appropriate users (usually just root) to modify parameters of the system.

Here is a sampling of the kinds of things that can be changed:

You changed some of these parameters in an earlier assignment. Were they being changed dynamicallly?
Adding Device Drivers

Device drivers are another element that makes monolithic kernels similar to microkernels. Device drivers all use a set of common interfaces to let the kernel communicate with a device. When a new particular device of some kind (such as a SCSI disk) is to be interfaced to a Linux OS for the first time, a driver must be written that allows it to work. Then the driver must be incorporated into a kernel for the device to be actually used by a running Linux machine.

It is not surprising that a new driver would need to be added to a Linux machine during its lifetime.

Many devices correspond to files in /dev. Network interfaces are the biggest exception to this rule. Every device file has a major and minor number. These are used to keep track of the location of device information in kernel tables.

The major number identifies the driver associated with the device. The minor (or unit) number (usually) keeps track of the individual device of that type. Executing ls -l will show you the device major and minor numbers.

The two broad groups of device files are block and character devices. Block devices transfer data on read or write in a group of bytes (usually a multiple of 512). Character devices can be read or written one byte at a time.

Some device drivers correspond to virtual devices. Consider for example PTY (pseudo-TTY) that are used to keep track of users who log in over the network. The actual device being used is the network interface, but having an abstraction layer corresponding to a terminal device for the login is quite convenient.

New device files are not created in the same way as regular files. One uses mknod to create a device. Debian and RedHat provide a script called /dev/MAKEDEV to provide an easy interface to using mknod. Check out its man page.

Nemeth notes that all Unix operating systems have device naming conventions that help one understand what device is being used. Some system's conventions are extremely detailed, some are relatively spartan. The following Linux device naming conventions hold:

In the cases of hard drives, the entire disk is made available as a device by droppping the partition number. Drivers can be added several ways: Unfortunately, it's quite common for devices to be changes using a kernel patch. To apply a kernel patch, one runs the patch program using a patch file that tracks the differences between the kernel before installation of the device and the kernel after installation of the device. The patch program will apply the changes to modify the kernel appropriately (if it really matched the before state). Then you have to recompile your kernel (see below).
Loading a Kernel Module

A kernel module is a program compiled separately from the kernel that can be dynamically loaded to extend the capabilities of the kernel.

Although properly installing a working kernel module will never cause your kernel to have problems, it's often the case that the first time you install a module it won't be installed properly or it just won't work. So don't install a module the first time on a production system running full-tilt.

Loadable kernel modules are usually stored in /lib/modules/version, where version is the version number of the Linux kernel. This can be found by executing uname -r.

The program lsmod will list the installed modules.

You install an LKM (loadable kernel module) by executing

insmod modulename [parameters ...] 

Modules can be unloaded using rmmod. If the number of references to the module (as shown by lsmod is not 0, however, executing rmmod will not actually have any effect.

The program modprobe knows about module dependencies, options, and installation/removal procedures and can be used to more sanely manage installation and removal of modules. The file /etc/modules.conf gives modprobe the configuration information it needs to operate properly.

Running modprobe -c will generate a file /etc/modules.conf that contains configuration information for all modules installed at the time of its running.

Recompiling a Kernel

To recompile the kernel you must engage in the following steps:

Nemeth's Sage Advice

If it ain't broke, don't fix it.

My more specific admonition: Don't mess with workin' code.

This advice is to be ignored by you while you have access to your machines. Please try out more stuff than you need to.

What Kernel Changes did Foster Make for Your Machines?

Most notably: