## 11.3. 接口特定的类型
内核中一些通常使用的数据类型有它们自己的 typedef 语句, 因此阻止了任何移植性问题. 例如, 一个进程标识符 ( pid ) 常常是 pid_t 而不是 int. 使用 pid_t 屏蔽了任何在实际数据类型上的不同. 我们使用接口特定的表达式来指一个类型, 由一个库定义的, 以便于提供一个接口给一个特定的数据结构.
注意, 在近期, 已经相对少定义新的接口特定类型. 使用 typedef 语句已经有许多内核开发者不喜欢, 它们宁愿看到代码中直接使用的真实类型信息, 不是藏在一个用户定义的类型后面. 很多老的接口特定的类型在内核中保留, 但是, 并且它们不会很快消失.
甚至当没有定义接口特定的类型, 以和内核其他部分保持一致的方式使用正确的数据类型是一直重要的. 一个嘀哒计数, 例如, 一直是 unsigned long, 独立于它实际的大小, 因此 unsigned long 类型应当在使用 jiffy 时一直使用. 本节我们集中于 _t 类型的使用.
很多 _t 类型在 <linux/types.h> 中定义, 但是列出来是很少有用. 当你需要一个特定类型, 你会在你需要调用的函数的原型中发现它, 或者在你使用的数据结构中.
无论何时你的驱动使用需要这样"定制"类型的函数并且你不遵照惯例, 编译器发出一个警告; 如果你使用 -Wall 编译器标志并且小心去除所有的警告, 你能有信心你的代码是可移植的.
_t 数据项的主要问题是当你需要打印它们时, 常常不容易选择正确的 printk 或 printf 格式, 你在一个体系上出现的警告会在另一个上重新出现. 例如, 你如何打印一个 size_t, 它在一些平台上是 unsigned long 而在其他某个上面是 unsigned int?
无论何时你需要打印某个接口特定的数据, 最好的方法是转换它的值为最大的可能类型(常常是 long 或者 unsigned long ) 并且接着打印它通过对应的格式. 这种调整不会产生错误或者警告, 因为格式匹配类型, 并且你不会丢失数据位, 因为这个转换或者是一个空操作或者是数据项向更大数据类型的扩展.
实际上, 我们在谈论的数据项不会常常要打印的, 因此这个问题只适用于调试信息. 常常, 代码只需要存储和比较接口特定的类型, 加上传递它们作为给库或者内核函数的参数.
尽管 _t 类型是大部分情况的正确解决方法, 有时正确的类型不存取. 这发生在某些还未被清理的老接口.
我们在内核头文件中发现的一个模糊之处是用在 I/O 函数的数据类型, 它松散地定义( 看第 9 章"平台相关性" 一节 ). 松散的类型在那里主要是因为历史原因, 但是在写代码时它可能产生问题. 例如, 交换给函数如 outb 的参数可能会有麻烦; 如果有一个 port_t 类型, 编译器会发现这个类型.
- Linux设备驱动第三版
- 第 1 章 设备驱动简介
- 1.1. 驱动程序的角色
- 1.2. 划分内核
- 1.3. 设备和模块的分类
- 1.4. 安全问题
- 1.5. 版本编号
- 1.6. 版权条款
- 1.7. 加入内核开发社团
- 1.8. 本书的内容
- 第 2 章 建立和运行模块
- 2.1. 设置你的测试系统
- 2.2. Hello World 模块
- 2.3. 内核模块相比于应用程序
- 2.4. 编译和加载
- 2.5. 内核符号表
- 2.6. 预备知识
- 2.7. 初始化和关停
- 2.8. 模块参数
- 2.9. 在用户空间做
- 2.10. 快速参考
- 第 3 章 字符驱动
- 3.1. scull 的设计
- 3.2. 主次编号
- 3.3. 一些重要数据结构
- 3.4. 字符设备注册
- 3.5. open 和 release
- 3.6. scull 的内存使用
- 3.7. 读和写
- 3.8. 使用新设备
- 3.9. 快速参考
- 第 4 章 调试技术
- 4.1. 内核中的调试支持
- 4.2. 用打印调试
- 4.3. 用查询来调试
- 4.4. 使用观察来调试
- 4.5. 调试系统故障
- 4.6. 调试器和相关工具
- 第 5 章 并发和竞争情况
- 5.1. scull 中的缺陷
- 5.2. 并发和它的管理
- 5.3. 旗标和互斥体
- 5.4. Completions 机制
- 5.5. 自旋锁
- 5.6. 锁陷阱
- 5.7. 加锁的各种选择
- 5.8. 快速参考
- 第 6 章 高级字符驱动操作
- 6.1. ioctl 接口
- 6.2. 阻塞 I/O
- 6.3. poll 和 select
- 6.4. 异步通知
- 6.5. 移位一个设备
- 6.6. 在一个设备文件上的存取控制
- 6.7. 快速参考
- 第 7 章 时间, 延时, 和延后工作
- 7.1. 测量时间流失
- 7.2. 获知当前时间
- 7.3. 延后执行
- 7.4. 内核定时器
- 7.5. Tasklets 机制
- 7.6. 工作队列
- 7.7. 快速参考
- 第 8 章 分配内存
- 8.1. kmalloc 的真实故事
- 8.2. 后备缓存
- 8.3. get_free_page 和其友
- 8.4. 每-CPU 的变量
- 8.5. 获得大量缓冲
- 8.6. 快速参考
- 第 9 章 与硬件通讯
- 9.1. I/O 端口和 I/O 内存
- 9.2. 使用 I/O 端口
- 9.3. 一个 I/O 端口例子
- 9.4. 使用 I/O 内存
- 9.5. 快速参考
- 第 10 章 中断处理
- 10.1. 准备并口
- 10.2. 安装一个中断处理
- 10.3. 前和后半部
- 10.4. 中断共享
- 10.5. 中断驱动 I/O
- 10.6. 快速参考
- 第 11 章 内核中的数据类型
- 11.1. 标准 C 类型的使用
- 11.2. 安排一个明确大小给数据项
- 11.3. 接口特定的类型
- 11.4. 其他移植性问题
- 11.5. 链表
- 11.6. 快速参考
- 第 12 章 PCI 驱动
- 12.1. PCI 接口
- 12.2. 回顾: ISA
- 12.3. PC/104 和 PC/104+
- 12.4. 其他的 PC 总线
- 12.5. SBus
- 12.6. NuBus 总线
- 12.7. 外部总线
- 12.8. 快速参考
- 第 13 章 USB 驱动
- 13.1. USB 设备基础知识
- 13.2. USB 和 sysfs
- 13.3. USB 的 Urbs
- 13.4. 编写一个 USB 驱动
- 13.5. 无 urb 的 USB 传送
- 13.6. 快速参考
- 第 14 章 Linux 设备模型
- 14.1. Kobjects, Ksets 和 Subsystems
- 14.2. 低级 sysfs 操作
- 14.3. 热插拔事件产生
- 14.4. 总线, 设备, 和驱动
- 14.5. 类
- 14.6. 集成起来
- 14.7. 热插拔
- 14.8. 处理固件
- 14.9. 快速参考
- 第 15 章 内存映射和 DMA
- 15.1. Linux 中的内存管理
- 15.2. mmap 设备操作
- 15.3. 进行直接 I/O
- 15.4. 直接内存存取
- 15.5. 快速参考
- 第 16 章 块驱动
- 16.1. 注册
- 16.2. 块设备操作
- 16.3. 请求处理
- 16.4. 一些其他的细节
- 16.5. 快速参考
- 第 17 章 网络驱动
- 17.1. snull 是如何设计的
- 17.2. 连接到内核
- 17.3. net_device 结构的详情
- 17.4. 打开与关闭
- 17.5. 报文传送
- 17.6. 报文接收
- 17.7. 中断处理
- 17.8. 接收中断缓解
- 17.9. 连接状态的改变
- 17.10. Socket 缓存
- 17.11. MAC 地址解析
- 17.12. 定制 ioctl 命令
- 17.13. 统计信息
- 17.14. 多播
- 17.15. 几个其他细节
- 17.16. 快速参考
- 第 18 章 TTY 驱动
- 18.1. 一个小 TTY 驱动
- 18.2. tty_driver 函数指针
- 18.3. TTY 线路设置
- 18.4. ioctls 函数
- 18.5. TTY 设备的 proc 和 sysfs 处理
- 18.6. tty_driver 结构的细节
- 18.7. tty_operaions 结构的细节
- 18.8. tty_struct 结构的细节
- 18.9. 快速参考