深入Linux内核架构—简介和概述(七)

一、数据类型

与用户层程序相比,内核对与数据类型有关的一些问题采取了不同的处理方法。

1、类型定义

内核使用typedef来定义各种数据类型,以避免依赖于体系结构相关的特性,比如,各个处理器上标准类型的位长可能都不见得相同。定义的类型名称如定义的类型名称如 sector_t (用于指定块设备上的扇区编号)、 pid_t (表示进程ID)等,这些都是由内核在特定于体系结构的代码中定义的,以确保相关类型的值落在适当的范围内。因为通常无须了解这些类型的定义是基于哪些基本的数据类型。

备注:

如果某个变量的类型是typedef而来的,则不能直接访问,而需要通过辅助函数。这样做确保了对相应数据类型的正确操作,尽管对用户来说类型定义其实是透明的。

在某些时候内核必须使用精确定义了位数的变量,例如,在需要向硬盘存储数据结构时。为允许数据在各种系统之间交换,无论数据在计算机内部如何表示,必须总是使用同样的外部格式。

为此,内核定义了若干整数数据类型,不仅明确标明了是有符号数还是无符号数,而且还指定了相关类型的精确位数。例如,__s8和__u8分别是有符号和无符号的8位整数。__u16和__s16、__u32和__s32、__u64和__s64的定义类似。

2、字节序

为表示数字,计算机采用大端或小端格式。该格式表示如何存储多个字节数据类型。

大端格式:数据在内存中存储时,先存储高位部分到低地址,再存储低位部分到高地址。

小端格式:数据在内存中存储时,先存储低位部分到低地址,再存储高位部分到高地址。

有些体系结构(如MIPS)支持两种字节序。

内核提供各种函数和宏,可以在CPU使用的格式与特定的表示法之间转换。cpu_to_le64将64位数据类型转换为小端格式,而le64_to_cpu所做的刚好相反。对16位、32位和64位的数据类型,所有的小端序、大端序之间的转换例程都可用。

le32_to_cpu is used for convesions from 32bit little endian data into CPUs endianness。

cpu_to_le32 is used for convesions from CPU endianness to little endian 32bit data。

3、per-cpu变量

普通的用户空间程序设计不会涉及的一个特殊事项就是所谓的per-cpu变量。它们是通过DEFINE_PER_CPU(type, name)声明,其中type是其数据类型(例如struct hash),name是变量名。在单处理器系统上,这与常规的变量声明没有不同。在有若干CPU的SMP系统上,会为每个CPU分别创建变量的一个实例。用于某个特定CPU的实例可以通过get_cpu(name, cpu)获得,其中smp_processor_id()返回当前活动处理器的ID,用作前述的cpu参数。

采用per-cpu变量好处:

所需数据很可能存在于处理器的缓存中,因此可以更快速地访问。如果在多处理器系统中使用可能被所有CPU同时访问的变量,可能会引发一些通信方面的问题,采用上述概念刚好绕过了这些问题。

4、访问用户空间

源代码中的多处指针都标记为__user,该标识符对用户空间程序设计是未知的。内核使用__user来标识指向用户地址空间中区域的指针,在没有进一步预防措施的情况下,不能轻易访问这些指针指向的区域。这是因为内存是通过页表映射到虚拟地址空间的用户空间部分的,而不是由物理内存直接映射的。因此内核需要确保指针所指向的页帧确实存在于物理内存中。

二、为什么内核是特别的

内核是一个庞大的C程序,带有一些汇编代码。内核吸引人,原因有几个。一、内核是由世界上最好的程序员编写的。二、结构良好,细节一丝不苟,巧妙的解决方案在代码中处处可见。尽管内核采用了设计得非常干净的抽象,以保持代码的模块化和易管理性,但这一点与内核的其他方面混合起来,使得代码非常有趣和独特。在必要的情况下,内核会以上下文相关的方式重用比特位置,多次重载结构成员,从指针已经对齐的部分压榨出又一个存储位,自由地使用 goto 语句,还有很多其他东西,这些都会使任何强调结构的程序员因痛苦而尖叫。

还有许多不同于用户层程序的严肃问题需要说明。

1、调试内核通常要比调试用户层程序困难。

2、内核提供了许多辅助函数,类似于用户空间的C语言库,但内核领域中的东西总是朴素得多。

3、用户层应用程序的错误可能会导致段错误(segmentation fault)或内存转储(core dump),但内核错误会导致整个系统故障。甚至更糟的是:内核会继续运行,在错误发生若干小时之后系统离奇地崩溃。如上所述,因为在内核空间调试比用户层应用程序更困难,所以在内核代码投入使用之前要进行更多的考虑。

4、必须考虑到内核运行的许多体系结构上不支持非对齐的内存访问。由于编译器插入的填充(padding)字段,也会影响到数据结构在不同体系结构之间的可移植性。

5、所有的内核代码都必须是并发安全的。由于对多处理器计算机的支持,Linux内核代码必须是可重入和线程安全的。也就是说,程序必须允许同时执行,而数据必须针对并行访问进行保护。

6、内核代码必须在小端序和大端序计算机上都能够工作。

7、大多数的体系结构根本不允许在内核中执行浮点计算,因此计算需要想办法用整型来替代。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值