You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Linux内核serdev总线的TTY翻转缓冲区锁死问题咨询

让我结合Linux TTY子系统和serdev总线的工作原理,一步步拆解你的问题:

问题1:TTY层是否会在读取完翻转缓冲区一半的所有数据前陷入阻塞?

准确来说,TTY层不会直接“阻塞”,但会陷入重复处理同一批未完成数据的死循环

Linux TTY的翻转缓冲区采用双缓冲设计:它有两个独立的缓冲区(flip->bufflip->buf2),当其中一个缓冲区填满(或达到触发阈值,通常是缓冲区大小的一半,比如4KB),TTY层就会调用你的接收回调处理这批数据。如果你的回调因为没找到终止符(0x00)而返回0,相当于告诉TTY层“我没处理任何数据”——这时TTY层会把这批数据留在缓冲区里,后续不断重复调用回调,尝试让你处理它。

而此时,另一个缓冲区可能已经接收了消息的剩余部分,但TTY层不会推送这部分数据给你——因为它认为上一批数据还没被处理。这种情况下,驱动看起来就像“锁死”了,TTY持续重复发送相同长度的已接收字节(就是第一个缓冲区里的那批未完成数据)。

问题2:如何避免该问题,且无需额外实现缓冲?

如果你不想在TTY缓冲区之上再构建独立的缓冲机制,最直接的方法是利用TTY子系统的规范模式(Canonical Mode),让TTY层自动帮你积累完整消息:

方案:配置TTY规范模式,将0x00设为消息终止符

TTY的规范模式本来就是基于“行分隔符”来交付完整数据的(默认是换行符),我们可以把终止符改成你的协议里的0x00,让TTY层自动积累数据,直到收到0x00才把完整消息交给你的驱动。在内核态的serdev驱动中,你可以这样实现:

struct tty_struct *tty = serdev_device_tty_get(serdev_dev);
if (tty) {
    struct ktermios *termios = &tty->termios;
    // 启用规范模式,禁用不需要的信号/回显逻辑
    termios->c_lflag |= ICANON;
    termios->c_lflag &= ~(ISIG | ECHO | ECHOE | ECHOK | ECHONL);
    // 将EOF字符设置为0x00,作为消息终止符
    termios->c_cc[VEOF] = 0x00;
    // 立即应用配置
    tty_set_termios(tty, termios);
    serdev_device_tty_put(serdev_dev);
}

配置完成后,TTY层会自动处理跨翻转缓冲区的消息:不管数据分在哪个缓冲区,它都会积累到收到0x00为止,再触发你的接收回调,交付完整的消息。你只需要在回调里处理完整消息,返回实际处理的字节数(包括0x00)即可,不用再手动扫描终止符。

另外还有一个轻量的备选方案:在接收回调中记录扫描进度。如果当前批次没找到终止符,返回你已经扫描过的字节数(而不是0),同时把扫描的结束位置记录在设备的私有数据里。等下一批数据进来时,从记录的位置继续扫描。这本质上只是跟踪一个状态变量,不算额外的缓冲机制,也能解决跨缓冲区的问题。

需要注意的是:serdev总线是基于TTY的,修改TTY模式前要确认设备的兼容性——有些serdev设备可能会强制使用原始模式,这时你需要调整适配逻辑。

内容的提问来源于stack exchange,提问作者evading

火山引擎 最新活动