11.1. Use of Standard C Types
Although most programmers are accustomed
to
freely using standard types like int and
long, writing device drivers requires some care to
avoid typing conflicts and obscure bugs.
The
problem is that you can't use the standard types
when you need "a 2-byte filler" or
"something representing a 4-byte
string," because the normal C data types are not the
same size on all architectures. To show the data size of the various
C types, the datasize program has been included
in the sample files provided on
O'Reilly's FTP site in the
directory misc-progs. This is a sample run of
the program on an i386 system (the last four types shown are
introduced in the next section):
morgana% misc-progs/datasize
arch Size: char short int long ptr long-long u8 u16 u32 u64
i686 1 2 4 4 4 8 1 2 4 8
The program can be used to show that long integers
and pointers feature a different size on 64-bit platforms, as
demonstrated by running the program on different Linux computers:
arch Size: char short int long ptr long-long u8 u16 u32 u64
i386 1 2 4 4 4 8 1 2 4 8
alpha 1 2 4 8 8 8 1 2 4 8
armv4l 1 2 4 4 4 8 1 2 4 8
ia64 1 2 4 8 8 8 1 2 4 8
m68k 1 2 4 4 4 8 1 2 4 8
mips 1 2 4 4 4 8 1 2 4 8
ppc 1 2 4 4 4 8 1 2 4 8
sparc 1 2 4 4 4 8 1 2 4 8
sparc64 1 2 4 4 4 8 1 2 4 8
x86_64 1 2 4 8 8 8 1 2 4 8
It's
interesting to note that the SPARC 64 architecture runs with a 32-bit
user space, so pointers are 32 bits wide there, even though they are
64 bits wide in kernel space. This can be verified by loading the
kdatasize module (available in the directory
misc-modules within the sample files). The
module reports size information at load time using
printk and returns an error (so
there's no need to unload it):
kernel: arch Size: char short int long ptr long-long u8 u16 u32 u64
kernel: sparc64 1 2 4 8 8 8 1 2 4 8
Although you must be careful
when mixing different data types, sometimes there are good reasons to
do so. One such situation is for memory addresses, which are special
as far as the kernel is concerned. Although, conceptually, addresses
are pointers, memory administration is often better accomplished by
using an unsigned integer type; the kernel treats physical memory
like a huge array, and a memory address is just an index into the
array. Furthermore, a pointer is easily dereferenced; when dealing
directly with memory addresses, you almost never want to dereference
them in this manner. Using an integer type prevents this
dereferencing, thus avoiding bugs. Therefore, generic memory
addresses in the kernel are usually unsigned
long, exploiting the fact that pointers and
long integers are always the same size, at least
on all the platforms currently supported by Linux.
For what it's
worth, the C99 standard defines the intptr_t and
uintptr_t types for an integer variable that can
hold a pointer value. These types are almost unused in the 2.6
kernel, however.
|