liunx源码探索。文件描述,流,tty,UART,port,dirver

要想理解网络就得理解Io模型要想理解Io模型就得理解流。

那什么是流。我的思路是从文件描述符还是聊起。

为什么对一个变量操作就可以实现真实的文件的读取和写入呢?

这得回到Liunx中的源码寻找线索

我们看这里的liunx源码。文件像一种流。说明流是文件的上层的概念‘

/* File is stream-like */

#define FMODE_STREAM

题主是第一次是看Liunx的源码。不禁感慨

光是看注释就有很多值得研究和思考的点了。

什么叫期望随机访问模式。

什么叫文件打开在这个什么O_PATH 路径,然后什么都做不了?为什么做不了?

什么叫文件的原子的访问?文件已经是进程之下的基础建设了。这里的原子指的什么样的概念。

回到起点,为什么讨论流就要聊到文件呢?

fs.h 和fstable.h分别是什么东西?

fs.h 是liunx 内核源码中记录文件系统的主接口

fstable.h 则侧重于文件系统类型注册与挂载表的实现和维护。

但准确来说这两个文件也仅仅停留在用户态的描述。

既然我们已经知道了流应该是文件更抽象的概念。我们就得去翻翻应该怎么描述我们的流。

上网查了下,这边给出来的判断是通过一个seek 的行为去区分的。

seek 是英文 “寻找(search/seek)” 的意思,在文件操作中,它表示:

修改当前读写位置(file position)

什么叫做修改当前读写位置呢?

注意出现在代码中的这个 FMODE_STREAM的标志位

然后我们上网可以看到这个标志位的代码

而设置了 FMODE_STREAM 的文件具有以下行为特征:

  • 不可寻址:不能使用 lseek() 等函数改变文件的读写位置。
  • 顺序读写:数据只能按顺序读取或写入,不能随机访问。
  • 实时性:数据通常是实时产生或消费的,如串口接收的数据流。

这些行为与传统的磁盘文件不同,磁盘文件支持随机访问,可以使用 lseek() 在文件中跳转到任意位置。

而要弄明白这个文件标志位的作用就得研究为什么会有这样的设计。这里的原因还得再tty在寻找。

探索tty?

揭开 TTY 的神秘面纱 (linusakesson.net)

我找到了一篇文章详细介绍了tty 的历史

所以流的概念本质上是与"行编辑" 和“会话管理”,“定向”,“上下文”, 这些行为相关的。

而其中行编辑还和一种叫行规程这样的东西扯在一块。不知道从哪里找到的一篇博客主。这个东西写的有点厉害

Linux终端和Line discipline图解-CSDN博客

CSDN博客+3Shall We Code?+3CSDN博客+3

总的来说还是看这几位大佬的博客中的图

Line Discipline 是 TTY 子系统中最复杂的部分,主要负责以下功能:

  • 行缓冲(Line Buffering):将用户输入的字符进行缓冲,直到用户按下回车键,才将整行数据提交给上层应用程序。
  • 行编辑(Line Editing):支持用户在输入过程中进行编辑操作,如退格(Backspace)、删除(Delete)、清除整行(Ctrl+U)等。
  • 回显(Echo):将用户输入的字符实时显示在终端上,提供即时反馈。
  • 信号处理(Signal Handling):识别特殊的控制字符,并向前台进程发送相应的信号,如 Ctrl+C 发送 SIGINT,Ctrl+Z 发送 SIGTSTP。
  • 会话管理(Session Management):维护前台和后台进程的关系,确保只有前台进程可以从终端读取输入。

这些功能主要由 drivers/tty/n_tty.c 文件中的 n_tty_ldisc_ops 结构体实现,该结构体定义了一系列操作函数,用于处理上述功能。

我们可以初步理解为流本质上是一种内核维护的人机交互接口

但是我们不禁问道如果要理解Liunx 做了什么。其他部分的硬件做了什么。这里就需要先了解一个叫行规程的东西。

因为源码中有高度的抽象。

比如这个路径下你会看到大量的什么加锁啊。线路引用啊。释放这些名词。并没有涉及到我们核心的读字节解释自己的部分。

/ drivers / tty / tty_ldisc.c

我们先找到下游一个n_tty_c

然后你会发现这里这个原先io.c然后太多分离出来

所以我们直接看

/drivers/tty/tty_io.c

在这个路径下

我们就会发现流的写入行为和内核缓存区和设备标识符相关

缓存区

神奇的是这里会有文件的写入

所以通过源码。我们可以判断。Linux 中“文件”(“文件描述符”)和“流”的行为之所以一致,是因为 Linux 内核在驱动层(tty)实现流行为的时候已经关联了文件结构体。使得上层看来流和文件的对象几乎是一致的。

if (tty_line >= 0 && tty_line < p->num && p->ops &&

p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {

res = tty_driver_kref_get(p);

e = tty_line;

break;

}

重点这段代码也就内核程序驱动程序然后驱动程序有一个poll部分我上一下这个轮询也就所谓驱动就是一套独立异步调用运行部分代码

然后我们可以看到一个数据结构tty-line 我上搜了一下叫做设备行(线单片知道

在硬件和协议设计中,一条通信线路(line)通常对应一个终端设备,于是“行号”就成了对具体设备的简称。

同样的我们可以相同文件下找到注册逻辑

我这边想法直接看串口部分设备行为代码我想这样代码关联肯定涉及到注册部分就可以论证猜想这种读写连接行为应该一体

然后我就看到一个有点陌生熟悉概念

uart?通信协议

然后我发现一层就是tty下面一层协议层uart也就是tty本质上硬件协议封装管理

看向这段涉及到所谓内存分配什么环形缓存分配

这段代码很关键我一开始以为这里只是初始化相关方法就没有留意全文这里几乎唯一一处声明寄存器相关的方法后续都是这个指针缓存交换操作

uart_change_line_settings()驱动框架调用具体串口硬件驱动设置寄存器的唯一路径,其核心调用是:

uport->ops->set_termios(uport, termios, old_termios);

termios查了一下这个结构体好像用来描述串口这个步骤通过这个结构写入波特率这样一些物理参数

准确来说 serial_core.c 实现一个uart协议管理所有串口设备然后不是串口设备直接寄存器这些东西打交道而是借助这个核心间接交道

这里有一段具体串口注册初始行为

我们同样核心core 找到 注册函数

normal = tty_alloc_driver(drv->nr, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);

重点这句tty_driver 这里注册

然后注意这里有条注释说明注册tty_driver 之后才会调用port()

我们找到了一个比较典型的串口注册来证明我们的猜想

追过来过去

一层

回到核心文件并且看到那个所谓one_port方法

不妨大胆追一层

我们发现这里也有tty

这里发生一个奇怪现象如果按照上面逻辑似乎进行tty-dirver注册再到port()注册调用

但是从 serial8250_register_8250_port()这里往上追uart_add_one_port serial_ctrl_register_portserial_core_register_port 好像又变成了从先注册port()再到tty()

我们别忘我们串口追上本身就是一个逆向过程也就是这段引用本质回调

我们目前从代码中得到的流程信息tty波特率初始化时候已经存在tty_dirver然后我们目标找到最终那个tty_line注册

好吧哥们这边代码探究能力实在有限

不过我们这边起码证实uart完成具体设备注册以及tty关联然后tty关联文件大致就是这么一个思路所以流的有效性和可靠性是依赖于tty的tty和设备中间的行为又是依赖于uart的也就是说本质上是uart协议保证了流的有效性。

所以总的协议栈就是。tty(行规程--SLIP) uart(协议)

那么还没有解决的问题就是我们知道文件在tty层发生类似于流这样的概念。但是我们不知道文件是如何脱离流或者说是描述tty的。这样才能够支持更加上层的设计。

接下来还是直接答案怎么调用吧

linux内核中串口驱动注册过程(tty驱动)_port.tty-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值