从Linux的tty_struct指针获取驱动上下文

背景

问题

前段时间开发一个tty驱动,用途是实现仪器对GPIB消息的接收、处理和上报。对于上报场景,下位机应用将上报内容写入一个驱动创建的tty设备,tty子系统将应用的输入转发给tty驱动,tty驱动将其转换成对SPI从设备(即GPIB扩展板)的写入,SPI从设备再将收到的SPI消息转换成GPIB消息发送给上位机。

实现tty_operations的write接口时,我是这么获取驱动上下文的:

c 复制代码
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:

c 复制代码
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宏:

c 复制代码
struct gpib_tty_ctx *ctx = container_of(tty->port, struct gpib_tty_ctx, port);

经测试,新的container_of宏返回了正确的驱动上下文地址。

总结

  1. tty_struct指针类似于file_operations接口的open方法的输出参数file指针,都对应内核自动分配的一个对象,其地址是不可以用于container_of宏的,但是它的成员private_data可以用于container_of宏,因为后者的值是驱动填写的。
  2. container_of宏的第一个参数是结构体成员的地址,这个结构体成员一般是个对象,不建议选地址类成员,因为如果是地址,则该成员很可能是复制过的,那么你通过给container_of宏提供二级指针(指针成员的地址就是二级指针)获取的ctx对象,很可能是错的。
相关推荐
自养Agent2 小时前
MCP Server Boundary Audit Report
linux·服务器·人工智能·windows·大模型·llm
Coisinier3 小时前
docker命令管理镜像及容器
linux·运维·nginx·docker·容器·操作系统
A心有千千结4 小时前
通过 SSH 反向代理让服务器借用本机网络访问 GitHub 的最佳实践
linux·服务器·网络·git·ssh·github
书生执笔画浮沉4 小时前
Linux 内核模块为什么不能自己解决兼容性问题?
linux·c·kernel·update·灵感
15Moonlight4 小时前
Linux网络(03):Socket 编程 TCP
linux·网络·tcp/ip
广州灵眸科技有限公司4 小时前
单芯片同时跑 Linux 与 RT-Thread:RV1126B的AMP异构方案完整拆解
linux·运维·服务器
小雪崩5 小时前
嵌入式学习:Linux驱动开发总结
linux·驱动开发·学习
C++ 老炮儿的技术栈5 小时前
std::atomic 是什么
linux·c语言·网络·c++·visual studio
2401_862880825 小时前
input 子系统框架 、platform 驱动框架
linux·驱动开发·嵌入式硬件
Liuqy-055 小时前
线程的互斥与同步机制——互斥锁、条件变量、无名信号量
linux·开发语言·线程·进程·条件变量·同步·互斥