设备驱动的私有数据设计
IIO子系统的私有数据
私有数据的完整使用流程:
// 定义私有数据结构
struct my_data {int calibration;u8 buffer[128];
};// 分配 IIO 设备(包含私有数据空间)
struct iio_dev *indio_dev = devm_iio_device_alloc(dev, sizeof(struct my_data));// 获取私有数据指针(关键步骤!)
struct my_data *data = iio_priv(indio_dev);// 初始化私有数据
data->calibration = 0x1234;
在Linux内核设计中(如Input,IIO子系统),通过以上代码操作私有数据的方式十分常见,他的本质是通过指针运算访问连续内存中的相邻区域,在devm_iio_device_alloc中,他会把堆空间设计为如下布局:
|--------------------------|-------------------------------|
| struct iio_dev (设备对象) | 私有数据 (private data) |
|--------------------------|-------------------------------|
^ ^
indio_dev iio_priv(indio_dev)
这样做能在初始化的时候把私有数据的大小确定,即指定私有数据的空间大小为my_data;而后的读写则通过访问固定偏移的地址即可
static inline void *iio_priv(const struct iio_dev *indio_dev)
{return (char *)indio_dev + ALIGN(sizeof(struct iio_dev), IIO_ALIGN);
}
对比包含void*的私有数据
Linux内核的IIO子系统(以及其他类似子系统)选择通过内存布局偏移量(如iio_priv())而不是在结构体中直接包含void*指针来访问私有数据,主要基于以下几个关键考虑:
- 内存效率优化
- 单次分配:通过连续内存布局,设备和私有数据可以一次性分配完成(只需一次kmalloc)
- 无额外指针开销:省去了void*指针本身占用的内存(在64位系统上节省8字节)
- 缓存友好性:设备和私有数据物理上相邻,提高CPU缓存命中率
// 当前方案(紧凑):
[iio_dev][padding][private_data]// 如果使用void*(额外开销):
[iio_dev][void*][private_data]
- 性能优势
-
减少内存访问:直接通过偏移量计算访问私有数据,省去了通过指针间接寻址的步骤
-
避免二级跳转:传统void*方案需要先读取指针值,再通过指针访问数据,而偏移量方案只需一次地址计算
但void也并非没有优点,相比固定偏移的方案,void能够处理更为灵活的场景,比如私有数据需要动态调整大小的场景;但在内核底层,偏移量方案的优势更为关键。