背景
问题
前段时间开发一个tty驱动,用途是实现仪器对GPIB消息的接收、处理和上报。对于上报场景,下位机应用将上报内容写入一个驱动创建的tty设备,tty子系统将应用的输入转发给tty驱动,tty驱动将其转换成对SPI从设备
(即GPIB扩展板)的写入,SPI从设备
再将收到的SPI消息转换成GPIB消息
发送给上位机
。
实现tty_operations
的write
接口时,我是这么获取驱动上下文
的:
struct gpib_tty_ctx {
struct tty_port port;
struct tty_struct mgr; // 其实是无效的成员
struct tty_driver *tty_drv;
struct spi_device *spi_dev;
struct gpib_spi_ctx *spi_ctx;
struct mutex tty_lock;
u32 activated:1;
const u8 *pend_tx_buf;
u32 pend_tx_len;
};
static int gpib_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
{
struct gpib_tty_ctx *ctx = container_of(tty, struct gpib_tty_ctx, mgr);
// 通过ctx指针访问上下文结构体gpib_tty_ctx的驱动私有字段,完成write功能
return count;
}
但是实际执行的时候,触发了空指针异常
,且空指针的值并不是0x0这种典型值,而是带一点偏移。
问题定位
经定位,是我对tty子系统的理解有问题,write
方法的第一个入参tty
,并不是gpib_tty_ctx
的struct tty_struct mgr
成员的地址,而是tty子系统在运行时自动创建的一个匿名tty_struct
对象的地址!因此我用container_of
宏获取到的gpib_tty_ctx
对象地址也是一个无效地址!
解决
注意到tty_struct
结构体包含一个类型为struct tty_port
的指针port
:
struct tty_struct {
struct kref kref;
int index;
struct device *dev;
struct tty_driver *driver;
struct tty_port *port; // 指向用户驱动创建并初始化的tty_port对象
const struct tty_operations *ops;
struct tty_ldisc *ldisc;
struct ld_semaphore ldisc_sem;
// ...
};
它应该就是指向驱动之前创建并初始化的gpib_tty_ctx.port
对象,这个对象本身是没有被复制的,因此我可以将这个指针传递给container_of
宏:
struct gpib_tty_ctx *ctx = container_of(tty->port, struct gpib_tty_ctx, port);
经测试,新的container_of
宏返回了正确的驱动上下文地址。
总结
tty_struct
指针类似于file_operations
接口的open
方法的输出参数file
指针,都对应内核自动分配的一个对象,其地址是不可以用于container_of
宏的,但是它的成员private_data
可以用于container_of
宏,因为后者的值是驱动填写的。container_of
宏的第一个参数是结构体成员
的地址
,这个结构体成员
一般是个对象,不建议选地址类成员,因为如果是地址,则该成员很可能是复制过的,那么你通过给container_of
宏提供二级指针(指针成员的地址就是二级指针)获取的ctx对象,很可能是错的。