< Previous PageNext Page > Hide TOC

Kernel Programming Style

As described in “Keep Out,” programming in the kernel is fraught with hazards that can cause instability, crashes, or security holes. In addition to these issues, programming in the kernel has the potential for compatibility problems. If you program only to the interfaces discussed in this document or other Apple documents, you will avoid the majority of these.

However, even limiting yourself to documented interfaces does not protect you from a handful of pitfalls. The biggest potential problem that you face is namespace collision, which occurs when your function, variable, or class name is the same as someone else’s. Since this makes one kernel extension or the other fail to load correctly (in a non-deterministic fashion), Apple has established function naming conventions for C and C++ code within the kernel. These are described in “Standard C Naming Conventions” and “C++ Naming Conventions,” respectively.

In addition to compatibility problems, kernel extensions that misbehave can also dramatically decrease the system’s overall performance or cause crashes. Some of these issues are described in “Performance and Stability Tips.” For more thorough coverage of performance and stability, you should also read the chapters “Security Considerations” and “Performance Considerations.”

C++ Naming Conventions

Basic I/O Kit C++ naming conventions are defined in the document I/O Kit Device Driver Design Guidelines. This section refines those conventions in ways that should make them more useful to you as a programmer.

Basic Conventions

The primary conventions are as follows:

This ensures that you will not collide with classes created by other companies or with future classes added to the operating system by Apple. It does not protect you from other projects created within your company, however, and for this reason, some additional guidelines are suggested.

Additional Guidelines

These additional guidelines are intended to minimize the chance of accidentally breaking your own software and to improve readability of code by developers. This is particularly of importance for open source projects.

These are only suggested guidelines. Your company or organization should adopt its own set of guidelines within the constraints of the basic conventions described in the previous section. These guidelines should provide a good starting point.

Standard C Naming Conventions

The naming conventions for C++ have been defined for some time in the document I/O Kit Device Driver Design Guidelines. However, no conventions have been given for standard C code. Because standard C has an even greater chance of namespace collision than C++, it is essential that you follow these guidelines when writing C code for use in the kernel.

Because C does not have the benefit of classes, it is much easier to run into a naming conflict between two functions. For this reason, the following conventions are suggested:

In short, picking any name that you would normally pick for a function is generally a bad idea, because every other developer writing code is likely to pick the same name for his or her function.

Occasional conflicts are a fact of life. However, by following these few simple rules, you should be able to avoid the majority of common namespace pitfalls.

Commonly Used Functions

One of the most common problems faced when programming in the kernel is use of “standard” functions—things like printf or bcopy. Many commonly used standard C library functions are implemented in the kernel. In order to use them, however, you need to include the appropriate prototypes, which may be different from the user space prototypes for those functions, and which generally have different names when included from kernel code.

In general, any non–I/O Kit header that you can safely include in the kernel is located in xnu/bsd/sys or xnu/osfmk/mach, although there are a few specialized headers in other places like libkern and libsa. Normal headers (those in /usr/include) cannot be used in the kernel.

Important: If you are writing an I/O Kit KEXT, most of these functions are not what you are looking for. The I/O Kit provides its own APIs for these features, including IOLog, IOMemoryDescriptor, and IOLock. While using the lower-level functionality is not expressly forbidden, it is generally discouraged (though printf is always fine). For more information about APIs available to I/O Kit KEXTs, see Kernel Framework Reference.

Table 6-1 lists some commonly used C functions, variables, and types, and gives the location of their prototypes.

Table 6-1  Commonly used C functions

Function name

Header path

printf

<sys/systm.h>

Buffer cache functions (bread, bwrite, and brelse)

<sys/buf.h>

Directory entries

<sys/dirent.h>

Error numbers

<sys/errno.h>

Kernel special variables

<sys/kernel.h>

Spinlocks

<sys/lock.h>

malloc

<sys/malloc.h>

Queues

<sys/queue.h>

Random number generator

<sys/rand.h>

bzero, bcopy, copyin, and copyout

<sys/systm.h>

timeout and untimeout

<sys/system.h>

Various time functions

<sys/time.h>

Standard type declarations

<sys/types.h>

<mach/mach_types.h>

User credentials

<sys/ucred.h>

OS and system information

<sys/utsname.h>

If the standard C function you are trying to use is not in one of these files, chances are the function is not supported for use within the kernel, and you need to implement your code in another way.

The symbols in these header files are divided among multiple symbol sets, depending on the technology area where they were designed to be used. To use these, you may have to declare dependencies on any of the following:

Where possible, you should specify a dependency on the KPI version of these symbols. However, these symbols are only available in v10.4 and later. For the I/O Kit and libkern, this should make little difference. For other areas, such as network kernel extensions or file system KEXTs, you must use the KPI versions if you want your extension to load in Mac OS X v10.4 and later.

For a complete list of symbols in any of these dependencies, run nm on the binaries in /System/Library/Extensions/System.kext/PlugIns.

Performance and Stability Tips

This section includes some basic tips on performance and stability. You should read the sections on security and performance for additional information. These tips cover only style issues, not general performance or stability issues.

Performance and Stability Tips

Programming in the kernel is subject to a number of restrictions that do not exist in application programming. The first and most important is the stack size. The kernel has a limited amount of space allocated for thread stacks, which can cause problems if you aren’t aware of the limitation. This means the following:

In addition to issues of stack size, you should also avoid doing anything that would generate unnecessary load such as polling a device or address. A good example is the use of mutexes rather than spinlocks. You should also structure your locks in such a way to minimize contention and to minimize hold times on the most highly contended locks.

Also, since unused memory (and particularly wired memory) can cause performance degradation, you should be careful to deallocate memory when it is no longer in use, and you should never allocate large regions of wired memory. This may be unavoidable in some applications, but should be avoided whenever possible and disposed of at the earliest possible opportunity. Allocating large contiguous blocks of memory at boot time is almost never acceptable, because it cannot be released.

There are a number of issues that you should consider when deciding whether to use floating point math or AltiVec vector math in the kernel.

First, the kernel takes a speed penalty whenever floating-point math or AltiVec instructions are used in a system call context (or other similar mechanisms where a user thread executes in a kernel context), as floating-point and AltiVec registers are only maintained when they are in use.

Note: In cases where altivec or floating point has already been used in user space in the calling thread, there is no additional penalty for using them in the kernel. Thus, for things like audio drivers, the above does not apply.

In general, you should avoid doing using floating-point math or AltiVec instructions in the kernel unless doing so will result in a significant speedup. It is not forbidden, but is strongly discouraged.

Second, AltiVec was not supported in the kernel prior to Mac OS X 10.3. It was not possible to detect this support from within the kernel until a later 10.3 software update. If you must deploy your KEXT on earlier versions of Mac OS X, you must either provide a non-AltiVec version of your code or perform the AltiVec instructions in user space.

Finally, AltiVec data stream instructions (dst, dstt, dstst, dss, and dssall) are not supported in the kernel, even for processors that support them in user space. Do not attempt to use them.

If you decide to use AltiVec in the kernel, your code can determine whether the CPU supports AltiVec using the sysctlbyname call to get the hw.optional.altivec property. For more information, see “The sysctlbyname System Call.”

Stability Tips

Style Summary

Kernel programming style is very much a matter of personal preference, and it is not practical to programmatically enforce the guidelines in this chapter. However, we strongly encourage you to follow these guidelines to the maximum extent possible. These guidelines were created based on frequent problems reported by developers writing code in the kernel. No one can force you to use good style in your programming, but if you do not, you do so at your own peril.



< Previous PageNext Page > Hide TOC


© 2002, 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-11-07)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.