处理器通用寄存器(GPR ’ s )的大小和它的字长是相同的。一般来说,对于一个的体系结构来说,它各个部件的宽度—比如说内存总线—最少要和它的字长一样大。而一般来说,地址空间的大小也等于字长,至少
Linux支持的体系结构中都是这样的
[不过实际上可寻址的内存空间也可能会比字长小一些。比如,一个64位的体系结构虽然可能会提供64位的指针,但可能只会用48位来寻址。此外,如果支持Intel的PAE,那么实际的物理内存也有比字长还大的可能。]。此外,C语言定义的long类型总对等于机器的字长,而int类型有时会比字长小。比如说,Alpha是64位机。所以它的寄存器、指针和long类型都是64位长度的,而int类型是32位的。Alpha机每一次可以访问和操作一个64位长的数据。
1.字、双字的含义
有些
操作系统和处理器不把它们的标准字长称作字,相反,出于历史原因和某种主观的命名习惯,它们用字来代表一些固定长度的
数据类型。比如说,一些系统根据长度把数据划分为字节(byte,8位),字(word,16位),双字(double words 32位)和四字(quad words 64位),而实际上该机是32位的。在本书中—在Linux中一般也是这样—象我们前面所讨论的那样,一个字就是代表处理器的字长。
对于支持的每一种体系结构,Linux都要将<asm/types.h>中的BITS_PER_LONG定义为C long类型的长度,也就是系统的字长。表1是Linux支持的体系结构和它们的字长的对照表。
表1 Linux支持的体系结构
体系结构 描 述 字 长 alpha Digital Alpha 64位 arm Arm and StrongArm 32位 cris CRIS 32位 h8300 H8/300 32位 i386 Intel x86 32位 ia64 IA-64 64位 m32r M32xxx 32位 m68k motorola 68k 32位 m68knommu M68k without MMU 32位 mips MIPS 32位 mips64 64位MIPS 64位 parisc HP PA_RISC 32位或64位 ppc PowerPC 32位 ppc64 POWER 64位 s390 IBM S/390 32位或64位 sh Hitachi SH 32位 sparc SPARC 32位 sparc64 UltraSPARC 64位 um Usermode Linux 32位或64位 v850 v850 32位 x86_64 x86-64 64位
|
C语言虽然规定了变量的最小长度,但是没有规定变量具体的标准长度,它们可以根据实现变化
[唯一的例外是char,它的长度总是8位]。C语言的标准数据类型长度随体系结构变化这一特性不断引起争议。好的一面是标准数据类型可以充分利用不同体系结构变化的字长而无需明确定义长度。C语言中long类型的长度就被确保为机器的字长。不好的一面是在编程时不能对标准的C数据类型进行大小的假定,没有什么能够保障int一定和long的长度是相同的
[事实上对于Linux支持的64位体系结构来说,long和int长度是不同的,int是32位的,而long是64位的。但对于我们所熟悉的32位体系结构而言,两种数据类型都是32位的]。
情况其实还会更加复杂,因为用户空间使用的数据类型和内核空间的数据类型不一定要相互关联。sparc64体系结构就提供了32位的用户空间,其中指针、int和long的长度都是32位。而在内核空间,它的int长度是32位,指针和long的长度却是64。没有什么标准来规范这些。
牢记下述准则:
* ANSI C标准规定,一个char的长度一定是8位。
*尽管没有规定int类型的长度是32位,但在Linux当前所有支持的体系结构中,它都是32位的。
* short类型也类似,在当前所有支持的体系结构中,虽然没有明文规定,但是它都是16位的(although no rule explicitly decrees that)。
*决不应该假定指针和long的长度,在Linux当前支持的体系结构中,它们就可以在32位和64位中变化。
*由于不同的体系结构long的长度不同,决不应该假设sizeof( int ) == sizeof( long )。
*类似地,也不要假设指针和int长度相等。
2. 不透明类型
不透明数据类型隐藏了它们内部格式或结构。在C语言中,它们就像黑盒一样。支持它们的语言不是很多。作为替代,开发者们利用typedef声明一个类型,把它叫做不透明类型,希望
其他人别去把它重新转化回对应的那个标准C类型。通常开发者们在定义一套特别的接口时才会用到它们。比如说用来保存进程标识符的pid_t类型。该类型的实际长度被隐藏起来了—尽管任何人都可以偷偷撩开它的面纱,发现它就是一个int。如果所有代码都不显式地利用它的长度(显式利用长度这里指直接使用int类型的长度,比如说在编程时使用sizeof(int)而不是sizeof(pid_t)—译者注),那么改变时就不会引起什么争议,这种改变确实可能会出现:在老版本的Unix系统中,pid_t的定义是short类型。
另外一个不透明数据类型的例子是atomic_t,它放置的是一个可以进行原子操作的整型值。尽管这种类型就是一个int,但利用不透明类型可以帮助确保这些数据只在特殊的有关原子操作的函数中才会被使用。不透明类型还帮助我们隐藏了类型的长度,但是该类型也并不总是完整的32位,比如在32位SPARC体系下长度被限制。
内核还用到了其他一些不透明类型,包括dev_t、gid_t和uid_t等等。处理不透明类型时的原则是:
*不要假设该类型的长度。
*不要将该类型转化回其对应的C标准类型使用。
*编程时要保证在该类型实际存储空间和格式发生变化时代码不受影响。
3. 指定数据类型
内核中还有一些数据虽然无需用不透明的类型表示,但它们被定义成了指定的数据类型。jiffy数目和在中断控制时用到的flags参数就是两个例子,它们都应该被存放在unsigned long类型中。
当存放和处理这些特别的数据时,一定要搞清楚它们对应的类型后再使用。把它们存放在其他如unsigned int等类型中是一种常见错误。在32位机上这没什么问题,可是64位机上就会捅娄子了。
4. 长度明确的类型
作为一个程序员,你往往需要在程序中使用长度明确的数据。像操作硬件设备,进行网络通信和操作二进制文件时,通常都必须满足它们明确的内部要求。比如说,一块声卡可能用的是32位寄存器,一个网络包有一个16位字段,一个可执行文件有8位的cookie。在这些情况下,数据对应的类型应该长度明确。
内核在<asm/typs.h>中定义了这些长度明确的类型,而该文件又被包含在文件<linux/types.h>中。表2有完整的清单。
表2 长度明确的数据类型
类型 描 述 s8 带符号字节 u8 无符号字节 s16 带符号16位整数 u16 无符号16位整数 s32 带符号32位整数 u32 无符号32位整数 s64 带符号64位整数 u64 无符号64位整数
|
其中带符号的变量用的比较少。
这些长度明确的类型大部分都是通过typedef对标准的C类型进行映射得到的。在一个64位机上,它们看起来像:
typedef signed char s8;
typedef unsigned char u8;
typedef signed short s16;
typedef unsigned short u16;
typedef signed int s32;
typedef unsigned int u32;
typedef signed long s64;
typedef unsigned long u64;
而在32位机上,它们可能定义成:
typedef signed char s8;
typedef unsigned char u8;
typedef signed short s16;
typedef unsigned short u16;
typedef signed int s32;
typedef unsigned int u32;
typedef signed long long s64;
typedef unsigned long long u64;
上述的这些类型只能在内核内使用,不可以在用户空间出现(比如,在头文件中的某个用户可见结构中出现)。这个限制是为了保护命名空间。不过内核对应这些不可见变量同时也定义了对应的用户可见的变量类型,这些类型与上面类型所不同的是增加了两个下画线前缀。比如,无符号32位整形对应的用户空间可见类型就是__u32。该类型除了名字有区别外,与u32相同。在内核中你可以任意使用这两个名字,但是如果是用户可见的类型,那你必须使用下画线前缀的版本名,防止污染用户空间的命名空间。
5. char型的符号问题
C标准表示char类型可以带符号也可以不带符号,由具体的编译器、处理器或由它们两者共同决定到底char是带符号合适还是不带符号合适。
大部分体系结构上,char默认是带符号的,它可以自-128到127之间取值。而也有一些例外,比如ARM体系结构上,char就是不带符号的,它的取值范围是0~255
举例来说,在默认char不带符号,下面的代码实际会把255而不是-1赋予i:
char i = -1;
而另一种机器上,默认char带符号,就会确切地把-1赋予i。如果程序员本意是把-1保存在i中,那么前面的代码就该修改成:
signed char i = -1;
另外,如果程序员确实希望存储255,那么代码应该如下:
unsigned char = 255;
如果你在自己的代码中使用了char类型,那么你要保证在带符号和不带符号的情况下代码都没问题。如果你能明确要用的是哪一个,那么就直接声明它。