Most of the commonly used data types in the kernel have their own
typedef
statements, thus preventing any portability
problems. For example, a process identifier (pid) is usually
pid_t
instead of int
. Using
pid_t
masks any possible difference in the actual
data typing. We use the expression
interface-specific to refer to a type defined by a
library in order to provide an interface to a specific data structure.
Even when no interface-specific type is defined, it’s always important
to use the proper data type in a way consistent with the rest of the
kernel. A jiffy count, for instance, is always unsigned long
, independent of its actual size, so the
unsigned long
type should always be used when
working with jiffies. In this section we concentrate on use of
“_t
” types.
The complete list of _t
types appears in
<linux/types.h>
, but the list is rarely
useful. When you need a specific type, you’ll find it in the
prototype of the functions you need to call or in the data structures
you use.
Whenever your driver uses functions that require such “custom” types and you don’t follow the convention, the compiler issues a warning; if you use the -Wall compiler flag and are careful to remove all the warnings, you can feel confident that your code is portable.
The main problem with _t
data items is that when
you need to print them, it’s not always easy to choose the right
printk or printf format, and
warnings you resolve on one architecture reappear on another. For
example, how would you print a size_t
, which is
unsigned long
on some platforms and
unsigned int
on some others?
Whenever you need to print some interface-specific data, the best way
to do it is by casting the value to the biggest possible type (usually
long
or unsigned long
) and then
printing it through the corresponding format. This kind of tweaking
won’t generate errors or warnings because the format matches the type,
and you won’t lose data bits because the cast is either a null
operation or an extension of the item to a bigger data type.
In practice, the data items we’re talking about aren’t usually meant to be printed, so the issue applies only to debugging messages. Most often, the code needs only to store and compare the interface-specific types, in addition to passing them as arguments to library or kernel functions.
Although _t
types are the correct solution for most
situations, sometimes the right type doesn’t exist. This happens for
some old interfaces that haven’t yet been cleaned up.
The one ambiguous point we’ve found in the kernel headers is data
typing for I/O functions, which is loosely defined (see Section 8.2.3 in Chapter 8). The loose
typing is mainly there for historical reasons, but it can create
problems when writing code. For example, one can get into trouble by
swapping the arguments to
functions like outb; if there were a
port_t
type, the compiler would find this type of
error.