您的位置:首页 > 健康 > 美食 > 网上购物平台口碑排名_专业做网站建设制作服务_汽车行业网站建设_网站建设有多少公司

网上购物平台口碑排名_专业做网站建设制作服务_汽车行业网站建设_网站建设有多少公司

2025/2/25 7:54:09 来源:https://blog.csdn.net/qq_43707773/article/details/145839955  浏览:    关键词:网上购物平台口碑排名_专业做网站建设制作服务_汽车行业网站建设_网站建设有多少公司
网上购物平台口碑排名_专业做网站建设制作服务_汽车行业网站建设_网站建设有多少公司

Linux驱动之tty子系统

深入linux源码探究!

1. TTY 子系统概述

在 Linux 内核中,TTY (Teletype) 子系统是 用户空间与字符设备交互的桥梁,它最早用于模拟电传打字机(Teletype),但如今主要用于 终端、串口、伪终端(PTY)等字符设备的管理。

TTY 子系统负责:

  • 字符设备驱动(如串口、虚拟终端等)的管理。
  • 终端 I/O 控制(如 stty 命令)。
  • 行规处理(输入缓冲、回显、信号处理等)。
  • 用户进程与设备的连接(如 bash 终端使用 /dev/tty)。

2. TTY 子系统架构

Linux TTY 子系统由 四层 组成:

  1. 用户空间 (User Space)
  • shell, minicom, cat /dev/ttyS0, ioctl()
  1. TTY 核心层 (TTY Core Layer)
  • tty_operations, tty_ldisc, tty_driver
  • TTY 核心层包含线路规程
  1. 线路规程 (Line Discipline)
  • N_TTY (标准终端), N_SLIP, N_PPP
  1. 低级驱动 (Low-level Drivers)
  • 串口 (serial), 虚拟终端 (virtual tty), 伪终端 (pty)

在这里插入图片描述

(1)用户空间

用户进程可以通过 /dev/ttyX 访问 TTY 设备,常见的方式包括:

  • 文件操作:open("/dev/ttyS0")read/write
  • IOCTL 控制:如 tcsetattr() 设置波特率、设置线路规程等。
  • 终端模拟器:如 minicomscreentmux

(2)TTY 核心层

TTY 核心层提供 TTY 设备的抽象接口,包括:

  • struct tty_driver:描述 TTY 设备的驱动信息。
  • struct tty_operations:TTY 设备操作函数,如 open()write()read()
  • struct tty_port:管理 TTY 端口,包括缓冲区、引用计数。
  • struct tty_ldisc:描述 ldisc线路规程(一般不增删、tty核心层已经做好不同线路规程的结构体实体)

(3)线路规程(Line Discipline)

  1. TTY 设备的输入数据可以通过不同的 线路规程 进行处理,在 Linux 中,有多个常见的线路规程。最常用的有:
  • N_TTY(默认)(TTY_LINE_DISC_TERMINAL):标准的文本终端处理规程。用于普通的字符终端设备,比如虚拟终端(tty、ttyS 等)。它负责处理普通的字符回显、行编辑、字符映射等。

  • N_PTY(TTY_LINE_DISC_PTY):伪终端处理规程。用于创建伪终端设备(如 tty 或 pty),用于实现终端模拟(如终端仿真器)。N_PTY 允许一个进程模拟另一个进程的终端行为。

  • N_SERIAL(TTY_LINE_DISC_SERIAL):用于串口通信的线路规程。该规程处理与串口设备(如 8250 串口)之间的通信,包括流控制、数据转换等。

  • N_UUCP:用于 UUCP(Unix-to-Unix Copy Protocol)通信的线路规程。它支持特定的通信协议,通常用于较早的拨号通信。

  • N_SG:用于高性能串口设备的线路规程。

  1. 一些常见的用于设置线路规划模式的 IOCTL 命令包括:
  • TIOCSETD:设置 TTY 设备的线路规划。
  • TIOCSERIAL:设置串口的相关属性(例如波特率、停止位等)。
  • TCSETA、TCSETAW、TCSETAF 等:设置终端的 termios 配置。
  1. 线路规程模式是在用户层指定tcsetattr() -> ioctl() -> 按照传递进来的CMD参数来创建不同的线路规程模式,并且添加到对应的tty_struct结构体中

(4)低级驱动

低级驱动负责具体设备的实现,包括:

  • 串口驱动:drivers/tty/serial/,如 8250 驱动。
  • 虚拟终端:drivers/tty/vt/,如 tty0tty1
  • 伪终端(PTY):drivers/tty/pty.c,用于 sshtmux 等。

3. TTY 关键数据结构

(1)TTY 驱动结构体 tty_driver

  • tty_driver**ttys**ports**cdevs都是在tty_alloc_driver函数中分配得的内存空间(分别是申请指向*tty_struct*port*cdev的数组,但是具体的porttty_structcdev并没有在这里分配)

定义在 include/linux/tty_driver.h

struct tty_driver {int	magic;		/* magic number for this structure */struct kref kref;	/* Reference management */struct cdev **cdevs;struct module	*owner;const char	*driver_name;const char	*name;int	name_base;	/* offset of printed name */int	major;		/* major device number */int	minor_start;	/* start of minor device number */unsigned int	num;	/* number of devices allocated */short	type;		/* type of tty driver */short	subtype;	/* subtype of tty driver */struct ktermios init_termios; /* Initial termios */unsigned long	flags;		/* tty driver flags */struct proc_dir_entry *proc_entry; /* /proc fs entry */struct tty_driver *other; /* only used for the PTY driver *//** Pointer to the tty data structures*/struct tty_struct **ttys;struct tty_port **ports;struct ktermios **termios;void *driver_state;    // 这个地方就是tty驱动层抽象底下不同设备的驱动的指针变量/** Driver methods*/const struct tty_operations *ops;struct list_head tty_drivers;
}
  1. 作用:
  • 注册 TTY 设备:使用 tty_register_driver()
  • 管理多个 TTY 端口:如 /dev/ttyS0, /dev/ttyS1
  • 存储具体设备的相关数据指针
    • void *driver_state;这个地方就是tty驱动层抽象底下不同设备的驱动的指针变量。
    • 比如在uart的驱动中就用driver_state这成员变量指向&uart_driver用来存储uart串口的信息,便于tty_operation中的操作集访问。
  • 一个tty_driver会对应多个tty_port
  • 在设备打开后,几乎操作的都是tty_struct来获取所有与进程和tty_port相关信息
  1. uart具体设备的一些结构体
  • struct uart_driver
struct uart_driver {struct module		*owner;const char		*driver_name;const char		*dev_name;int			 major;int			 minor;int			 nr; //串口数量struct console		*cons;struct uart_state	*state;struct tty_driver	*tty_driver;
};
  • struct uart_state
struct uart_state {struct tty_port		port;enum uart_pm_state	pm_state;struct circ_buf		xmit;atomic_t		refcount;wait_queue_head_t	remove_wait;struct uart_port	*uart_port;
};
  • struct uart_port
struct uart_port {...struct uart_state       *state; ...const struct uart_ops   *ops;...unsigned int            line;     /* port index */...
};

(2)TTY 设备操作 tty_operations

  1. tty_operations是tty核心层暴露给具体设备操作集接口,注册申请tty设备需要实现tty_operations结构体中的函数集。
  • 比如uart驱动中就实现了tty_operations操作集
  • 如果需要注册一个tty设备就得实现tty_operations操作集
  1. 定义
    定义在 include/linux/tty_driver.h
struct tty_operations {int (*open)(struct tty_struct *tty, struct file *filp);void (*close)(struct tty_struct *tty, struct file *filp);int (*write)(struct tty_struct *tty, const unsigned char *buf, int count);...
};
  1. tty_operations操作集的具体用到的地方在本文第4节《tty框架的write/read流程》中的图片中有标注。

  2. 常见操作:

  • open():打开 TTY 设备。
  • close():关闭 TTY 设备。
  • write():写入数据到 TTY 设备。

(3)TTY 端口 tty_port

  1. TTY 端口管理,如缓冲区、状态:

  2. tty_port 需要手动分配和初始化,并且tty_port初始化后必须挂在到tty_driverports[i]中,或者挂在到tty_struct中的port字段,否则会引起crash the kernel,(在uart驱动中,已经将tty_port添加tty_struct中的port字段中了),

  3. 或者在需要操作tty_port的地方能够正确找到创建的tty_port,比如在tty_operations的open中就需要 tty_port_open()来传入tty_port*

  4. tty_port_install()中可以将tty_struct中的port字段和tty_port关联

  5. 重要的是tty_struct中的port字段需要被填充struct port*,如下(在tty_init_dev()中):

if (!tty->port)tty->port = driver->ports[idx];
struct tty_port {struct tty_bufhead	buf;		/* Locked internally */struct tty_struct	*tty;		/* Back pointer */struct tty_struct	*itty;		/* internal back ptr */const struct tty_port_operations *ops;	/* Port operations */const struct tty_port_client_operations *client_ops; /* Port client operations */spinlock_t		lock;		/* Lock protecting tty field */int			blocked_open;	/* Waiting to open */int			count;		/* Usage count */wait_queue_head_t	open_wait;	/* Open waiters */wait_queue_head_t	delta_msr_wait;	/* Modem status change */unsigned long		flags;		/* User TTY flags ASYNC_ */unsigned long		iflags;		/* Internal flags TTY_PORT_ */unsigned char		console:1,	/* port is a console */low_latency:1;	/* optional: tune for latency */struct mutex		mutex;		/* Locking */struct mutex		buf_mutex;	/* Buffer alloc lock */unsigned char		*xmit_buf;	/* Optional buffer */unsigned int		close_delay;	/* Close port delay */unsigned int		closing_wait;	/* Delay for output */int			drain_delay;	/* Set to zero if no pure timebased drain is needed elseset to size of fifo */struct kref		kref;		/* Ref counter */void 			*client_data;
};

(4)TTY 结构体 tty_struct

  • tty_struct是整个tty子系统管理的核心结构体,负责将进程、tty_drivertty_port联系起来。

  • tty_struct由 TTY 核心在打开 TTY 设备时在tty_init_dev中自动创建tty_struct,并添加到tty_driver*ttys[tty->index]中。

  • tty_port->tty 并不会存储所有 tty_struct,它只是一个指针,指向最近一次成功打开的 tty_struct

4. tty框架的write/read流程

在这里插入图片描述

上面的write/read是在linux源码一步一步跳转查看的。具体流程应该差不多。

5. 简单虚拟tty设备框架

  • 这里的tty_port是直接创建初始化,并挂在在tty_driver->ports[i]中,并没有在其他install(tty_operations)的接口上把tty_port挂载到tty->port上。
  • 也可以实现install接口来实现把tty_port挂载到tty->port上。
  1. 代码说明:
  • 这里代码会生成/dev/ttyPCIE0/dev/ttyPCIE1设备。
  • 向其中一个设备写入数据会写入到另一个设备中,可以从另一个设备读取。
  • 可以用minicom工具打开测试
    • sudo minicom -D /dev/ttyPCIE0
    • sudo minicom -D /dev/ttyPCIE1
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>#define TTY_MAJOR_AUTO 0
#define VIRTUAL_TTY_MINORS 2
#define DRIVER_NAME "gps_pcie_tty"static struct tty_driver *gps_pcie_tty_driver;
static struct tty_port *gps_pcie_tty_ports[VIRTUAL_TTY_MINORS] = {NULL, NULL};static const struct tty_port_operations gps_pcie_tty_port_ops = {};static int gps_pcie_tty_open(struct tty_struct *tty, struct file *filp)
{int port_num = tty->index;if (port_num >= VIRTUAL_TTY_MINORS)return -ENODEV;// 设置 tty->porttty->port = gps_pcie_tty_ports[port_num];// 打开端口return tty_port_open(tty->port, tty, filp);
}static void gps_pcie_tty_close(struct tty_struct *tty, struct file *filp)
{tty_port_close(tty->port, tty, filp);
}static int gps_pcie_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
{int port_num = tty->index;int other_port_num = (port_num == 0) ? 1 : 0;struct tty_port *other_port = gps_pcie_tty_ports[other_port_num];struct tty_struct *other_tty = tty_port_tty_get(other_port);// 另一个tty设备if (other_tty) {// 向上递交数据tty_insert_flip_string(other_port, buf, count);tty_flip_buffer_push(other_port);tty_kref_put(other_tty);}return count;
}static const struct tty_operations gps_pcie_tty_ops = {.open = gps_pcie_tty_open,.close = gps_pcie_tty_close,.write = gps_pcie_tty_write,
};static int __init gps_pcie_tty_init(void)
{int ret;int i;gps_pcie_tty_driver = tty_alloc_driver(VIRTUAL_TTY_MINORS, TTY_DRIVER_REAL_RAW);if (IS_ERR(gps_pcie_tty_driver))return PTR_ERR(gps_pcie_tty_driver);gps_pcie_tty_driver->owner = THIS_MODULE;gps_pcie_tty_driver->driver_name = DRIVER_NAME;gps_pcie_tty_driver->name = "ttyPCIE";gps_pcie_tty_driver->major = TTY_MAJOR_AUTO;gps_pcie_tty_driver->minor_start = 0;gps_pcie_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;gps_pcie_tty_driver->subtype = SERIAL_TYPE_NORMAL;gps_pcie_tty_driver->init_termios = tty_std_termios;gps_pcie_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;tty_set_operations(gps_pcie_tty_driver, &gps_pcie_tty_ops);for (i = 0; i < VIRTUAL_TTY_MINORS; i++) {gps_pcie_tty_ports[i] = kzalloc(sizeof(struct tty_port), GFP_KERNEL);if (!gps_pcie_tty_ports[i]) {ret = -ENOMEM;goto err_free_ports;}tty_port_init(gps_pcie_tty_ports[i]);gps_pcie_tty_ports[i]->ops = &gps_pcie_tty_port_ops;gps_pcie_tty_driver->ports[i] = gps_pcie_tty_ports[i];}ret = tty_register_driver(gps_pcie_tty_driver);if (ret) {goto err_free_ports;}pr_info("gps_pcie_tty loaded\n");return 0;err_free_ports:for (i = 0; i < VIRTUAL_TTY_MINORS; i++) {if (gps_pcie_tty_ports[i]){kfree(gps_pcie_tty_ports[i]);gps_pcie_tty_ports[i] = NULL;}}if(gps_pcie_tty_driver){tty_driver_kref_put(gps_pcie_tty_driver);}return ret;
}static void __exit gps_pcie_tty_exit(void)
{int i;tty_unregister_driver(gps_pcie_tty_driver);for (i = 0; i < VIRTUAL_TTY_MINORS; i++) {if (gps_pcie_tty_ports[i] != NULL){tty_port_tty_hangup(gps_pcie_tty_ports[i], false);tty_port_destroy(gps_pcie_tty_ports[i]);kfree(gps_pcie_tty_ports[i]);gps_pcie_tty_ports[i] = NULL;}}if(gps_pcie_tty_driver){tty_driver_kref_put(gps_pcie_tty_driver);}gps_pcie_tty_driver = NULL;pr_info("gps_pcie_tty driver unloaded\n");
}module_init(gps_pcie_tty_init);
module_exit(gps_pcie_tty_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("One Code");
MODULE_DESCRIPTION("One-Code PCIE TTY DRIVER");

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com