4. 核心数据结构
在 VxWorks 字符设备驱动中,核心数据结构是驱动框架的骨架。理解 DEV_HDR、设备私有结构体、文件描述符表 以及 驱动函数表 (DRV_ENTRY) 之间的关系,是编写正确、可并发访问驱动的第一步。本节将逐一剖析这些结构的设计意图与内存布局。
4.1 DEV_HDR:设备对象的标准头部
DEV_HDR 是 VxWorks I/O 系统中所有设备对象的公共头部。它必须作为设备结构体的第一个成员出现,使得 I/O 子系统能够通过统一的指针操作来识别和管理设备。
| 成员 | 类型 | 描述 |
|---|---|---|
| drvNum | int |
该设备所属的驱动编号,由 iosDrvInstall() 分配 |
| devName | char * |
设备名称字符串指针,如 "/tyCo/0" |
| type | int |
设备类型标识,用于区分字符设备、块设备等 |
| reserved | int |
保留字段,通常用于对齐或扩展 |
关键设计原则: 驱动开发者自定义的设备结构体必须以
DEV_HDR 开头。通过强制类型转换,I/O 系统可以将任何设备指针视为 DEV_HDR * 来获取驱动号和设备名。
4.2 设备私有结构体设计
设备私有结构体用于存储每个设备实例的独有状态。在并发访问场景下,私有结构体是锁机制的主要载体。一个典型的设计包含同步原语、硬件资源和状态标志。
/* 设备私有结构体示例 */
typedef struct my_dev
{
DEV_HDR devHdr; /* 标准设备头部,必须为第一成员 */
SEM_ID semLock; /* 互斥信号量,保护临界区 */
SEM_ID semBufFull; /* 同步信号量,缓冲区满 */
SEM_ID semBufEmpty; /* 同步信号量,缓冲区空 */
char * pBuf; /* 数据缓冲区指针 */
int bufSize; /* 缓冲区大小 */
int rdIdx; /* 读索引 */
int wrIdx; /* 写索引 */
BOOL isOpen; /* 设备打开标志 */
int openCount; /* 打开计数,用于多任务共享 */
} MY_DEV;
设计建议: 将信号量、互斥量等同步原语直接嵌入私有结构体,而非使用全局变量。这保证了每个设备实例拥有独立的锁域,是实现设备级并发隔离的基础。
4.3 文件描述符表 (Fd Table)
文件描述符表是 VxWorks I/O 系统维护的全局表,用于将用户态的文件描述符 (fd) 映射到具体的设备对象和驱动函数。每个打开的文件描述符对应一个文件描述符表项。
| 表项字段 | 类型 | 说明 |
|---|---|---|
| pDevHdr | DEV_HDR * |
指向设备对象的指针 |
| pDrvEntry | DRV_ENTRY * |
指向驱动函数表的指针 |
| value | int |
文件描述符值,用于快速索引 |
| options | int |
打开时的选项标志 (O_RDONLY, O_NONBLOCK 等) |
| taskId | int |
打开该文件描述符的任务 ID |
并发风险: 多个任务可以同时持有指向同一个
DEV_HDR 的文件描述符。这意味着驱动函数中的 pDevHdr 参数可能被多个任务并发访问。驱动开发者必须假设 open()、read()、write() 等函数会在不同任务上下文中同时执行。
4.4 驱动函数表 (DRV_ENTRY)
DRV_ENTRY 是驱动函数表的结构体类型,它定义了驱动需要实现的七个标准 I/O 函数入口。驱动通过 iosDrvInstall() 将函数表注册到 I/O 系统。
/* DRV_ENTRY 结构体定义 */
typedef struct drv_entry
{
int (*create) (); /* 设备创建函数 */
int (*remove) (); /* 设备移除函数 */
int (*open) (); /* 设备打开函数 */
int (*close) (); /* 设备关闭函数 */
int (*read) (); /* 设备读取函数 */
int (*write) (); /* 设备写入函数 */
int (*ioctl) (); /* 设备控制函数 */
} DRV_ENTRY;
| 函数指针 | 典型并发问题 | 锁策略 |
|---|---|---|
| open | 多任务同时打开同一设备 | 使用引用计数 + 互斥锁 |
| close | 关闭时仍有其他任务在读写 | 等待所有读写完成后再释放 |
| read / write | 缓冲区竞争、索引错乱 | 互斥锁保护缓冲区操作 |
| ioctl | 配置变更与读写并发 | 读写锁或互斥锁 |
调试要点: 在调试并发问题时,可以在每个驱动函数的入口和出口处添加日志,打印当前任务 ID 和获取到的锁状态。这有助于快速定位死锁或锁未释放的场景。
4.5 数据结构关系总结
四个核心数据结构构成了 VxWorks 字符设备驱动的完整链路:
- DEV_HDR 是设备对象的身份证,提供驱动号和设备名。
- 设备私有结构体 是设备的具体实现,包含锁、缓冲区和状态。
- 文件描述符表 是用户态与内核态之间的桥梁,实现 fd 到设备对象的映射。
- DRV_ENTRY 是驱动行为的定义表,I/O 系统通过它调用具体的驱动函数。
常见错误: 在驱动函数中直接使用全局变量而非通过
pDevHdr 获取私有结构体。这会导致多设备实例时数据错乱。正确的做法是使用 container_of 或强制类型转换从 DEV_HDR * 得到私有结构体指针。