Linux TTY介绍
1. TTY介绍
TTY(TeleType)指Linux中的一类终端(Terminal)设备, 是一种字符设备
在Linux中, tty可分为如下几类
- 串行端口终端(serial port terminal): 指使用计算机串行端口连接的终端设备, /dev/ttySn
- 伪终端(pseudo terminal): 通常是通过ssh登陆的终端, /dev/pts/*
- 控制终端(controlling terminal): 代表当前tty设备 /dev/tty
- 控制台终端(console): 指计算机的输出设备, 通常是printk信息输出的设备, /dev/ttyn、/dev/console
详细定义如下
/* tty driver types */
#define TTY_DRIVER_TYPE_SYSTEM 0x0001
#define TTY_DRIVER_TYPE_CONSOLE 0x0002
#define TTY_DRIVER_TYPE_SERIAL 0x0003
#define TTY_DRIVER_TYPE_PTY 0x0004
#define TTY_DRIVER_TYPE_SCC 0x0005
/* scc driver */
#define TTY_DRIVER_TYPE_SYSCONS 0x0006
tty可以分为如下几层
- 核心层(tty core): 是tty设备的抽象
- 线路规程(tty line discipline): 是对上层和底层之间数据传输的协议转换, 不同类型的终端设备数据转换协议不同
- 驱动层(tty driver): 面向底层硬件的设备驱动
tty代码位于drivers/tty目录下
tty软件框架如图所示:
2. TTY初始化
在系统启动过程中, 注册了/dev/tty和/dev/console设备
chr_dev_init()
tty_init()
/* 注册/dev/tty设备 */
cdev_init(&tty_cdev, &tty_fops);
cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1)
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty")
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty")
/* 注册/dev/console设备 */ cdev_init(&console_cdev, &console_fops);
cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1)
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console")
consdev = device_create_with_groups(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, cons_dev_groups, "console"); start_kernel()
console_init()
/* 注册N_TTY线路规程 */
n_tty_init()
tty_register_ldisc(N_TTY, &n_tty_ops);
其中, 值得一说的是tty_fops, 定义了tty设备文件操作集
static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = tty_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
};
3. TTY接口
驱动相关API主要如下:
/* 分配tty驱动数据结构 */
struct tty_driver *alloc_tty_driver(unsigned int lines);
struct tty_driver *tty_alloc_driver(unsigned int lines, unsigned long flags)
/* 注册/释放tty驱动 */
int tty_register_driver(struct tty_driver *driver);
int tty_unregister_driver(struct tty_driver *driver);
/* 释放tty驱动 */
void put_tty_driver(struct tty_driver *d);
/* tty端口销毁 */
void tty_port_destroy(struct tty_port *port);
/* 设置tty文件操作集 */
void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op);
alloc_tty_driver/tty_alloc_driver完成了如下事宜
1. 分配tty_driver数据结构
2. 初始化tty_driver的magic、num、owner、flags成员
3. 如果flags不包含TTY_DRIVER_DEVPTS_MEM, 分配num个tty_struct和ktermios指针变量(!!!注意只是分配了指针!!!)
4. 如果flags不包含TTY_DRIVER_DYNAMIC_ALLOC, 分配num个tty_port指针变量(!!!注意只是分配了指针!!!)
5. 分配cdev数据结构
tty_register_driver完成了如下事宜
1. 如果指定了主设备号, 通过register_chrdev_region静态申请设备号; 否则通过alloc_chrdev_region动态分配设备号
2. 如果flags包含TTY_DRIVER_DYNAMIC_ALLOC, 调用tty_cdev_add分配并注册字符设备对象
2.1 tty_cdev_add通过cdev_alloc动态分配字符设备对象并赋值给tty_driver的cdevs, 并将tty_fops赋值给cdev的ops, 最后通过cdev_add注册字符设备对象
3. 通过list_add将tty_driver变量添加到tty_drivers链表
4. 如果flags不包含TTY_DRIVER_DYNAMIC_DEV, 通过tty_register_device注册num个tty设备
5. 调用proc_tty_register_driver注册/proc/tty/driver/*
设备相关API主要如下:
/* tty设备注册 */
struct device *tty_register_device(struct tty_driver *driver, unsigned index, struct device *dev);
struct device *tty_port_register_device(struct tty_port *port, struct tty_driver *driver, unsigned index, struct device *device);
struct device *tty_port_register_device_attr(struct tty_port *port, struct tty_driver *driver, unsigned index, struct device *device, void *drvdata, const struct attribute_group **attr_grp);
/* tty设备注销 */
void tty_unregister_device(struct tty_driver *driver, unsigned index);
tty_port_register_device完成了如下事宜
1. 调用tty_port_link_device将tty_port赋值到tty_driver的ports变量
2. 调用tty_register_device_attr注册tty设备
2.1 根据tty_driver的type调用pty_line_name或者tty_line_name生成tty名称
2.2 如果tty_driver的flags包含TTY_DRIVER_DYNAMIC_ALLOC, 调用tty_cdev_add分配并注册字符设备对象
2.2.1 tty_cdev_add通过cdev_alloc动态分配字符设备对象并赋值给tty_driver的cdevs, 并将tty_fops赋值给cdev的ops, 最后通过cdev_add注册字符设备对象
2.3 分配device数据结构, 并赋值devt、class、parent、release、groups成员; 通过dev_set_name设置device设备名称; 通过dev_set_drvdata设置device的成员变量driver_data, 对于uart驱动来说为tty_port变量
2.4 通过device_register向系统注册设备
4. TTY数据结构
tty核心层包含如下几个重要数据结构
tty_struct是tty设备的"动态抽象", 和文件句柄的功能类似, 还保存了tty设备生命周期中的临时信息, 其生命周期为从打开tty设备开始, 到关闭tty设备结束; 主要供核心层使用
struct tty_struct {
int magic;
struct kref kref;
struct device *dev; /* 指向tty设备 */
struct tty_driver *driver; /* 指向tty驱动 */
const struct tty_operations *ops; /* 指向tty_driver::tty_operations */
int index; /* 指向tty设备的编号(如tty0、tty1中的0、1) */ struct ld_semaphore ldisc_sem;
struct tty_ldisc *ldisc; /* 该tty对应的线路规程 */ struct mutex atomic_write_lock;
struct mutex legacy_mutex;
struct mutex throttle_mutex;
struct rw_semaphore termios_rwsem;
struct mutex winsize_mutex;
spinlock_t ctrl_lock;
spinlock_t flow_lock;
/* Termios values are protected by the termios rwsem */
struct ktermios termios, termios_locked;
struct termiox *termiox; /* May be NULL for unsupported */
char name[];
struct pid *pgrp; /* Protected by ctrl lock */
struct pid *session;
unsigned long flags;
int count;
struct winsize winsize; /* winsize_mutex */
unsigned long stopped:, /* flow_lock */
flow_stopped:,
unused:BITS_PER_LONG - ;
int hw_stopped;
unsigned long ctrl_status:, /* ctrl_lock */
packet:,
unused_ctrl:BITS_PER_LONG - ;
unsigned int receive_room; /* Bytes free for queue */
int flow_change; struct tty_struct *link;
struct fasync_struct *fasync;
int alt_speed; /* For magic substitution of 38400 bps */
wait_queue_head_t write_wait;
wait_queue_head_t read_wait;
struct work_struct hangup_work;
void *disc_data; /* 指向n_tty_data, 用于保存线路规程缓冲区信息 */
void *driver_data; /* 保存tty驱动数据, 对于uart为uart_state*/
spinlock_t files_lock; /* protects tty_files list */
struct list_head tty_files; int closing;
unsigned char *write_buf;
int write_cnt;
/* If the tty has a pending do_SAK, queue it here - akpm */
struct work_struct SAK_work;
struct tty_port *port; /* 指向tty端口 */
};
tty_driver是tty设备的主要数据结构
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; /* 该tty驱动的名称, 在tty内部使用 */
const char *name; /* 该tty驱动的设备名称, 体现到sysfs以及/dev/等文件系统下 */
int name_base; /* offset of printed name */
int major; /* 主设备号, 如TTY_MAJOR */
int minor_start; /* 起始次设备号, 通常从64开始, why? */
unsigned int num; /* tty驱动对应设备数 */
short type; /* tty驱动类型, 如SERIAL、PTY等 */
short subtype; /* tty驱动子类型*/
struct ktermios init_termios; /* 初始termios, 如tty_std_termios */
unsigned long flags; /* tty驱动flags */
struct proc_dir_entry *proc_entry; /* /proc文件系统入口 */
struct tty_driver *other; /* 只用于PTY驱动 */ /*
* 指向tty相关数据结构
*/
struct tty_struct **ttys;
struct tty_port **ports;
struct ktermios **termios;
void *driver_state; /* tty驱动私有数据 */ const struct tty_operations *ops; /* tty文件操作集 */
struct list_head tty_drivers;
};
tty_port是tty设备固有属性的"静态抽象", 保存了该tty设备的一些固定的属性, 供具体的tty驱动使用
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 */
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:, /* port is a console */
low_latency:; /* 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 time
based drain is needed else
set to size of fifo */
struct kref kref; /* Ref counter */
};
termios, 定义了POSIX终端接口操作对象; 在用户空间为struct termios, 内核空间为struct ktermios
struct ktermios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* tty设备输入速率 */
speed_t c_ospeed; /* tty设备输出速率 */
};
tty_operations定义了硬件有关的操作, 由tty驱动实现, 被tty核心层调用
struct tty_operations {
struct tty_struct *(*lookup)(struct tty_driver *, struct file *, int);
int (*install)(struct tty_driver *, struct tty_struct *);
void (*remove)(struct tty_driver *, struct tty_struct *);
int (*open)(struct tty_struct *, struct file *); /*打开
*/
void (*close)(struct tty_struct *, struct file *); /* 关闭 */
void (*shutdown)(struct tty_struct *);
void (*cleanup)(struct tty_struct *);
int (*write)(struct tty_struct *, const unsigned char *, int); /* 写入缓冲区并刷新到硬件 */
int (*put_char)(struct tty_struct *, unsigned char); /* 单字节写入缓冲区 */
void (*flush_chars)(struct tty_struct *); /* 刷新写缓冲区到硬件 */
int (*write_room)(struct tty_struct *); /* 写缓冲区空闲大小 */
int (*chars_in_buffer)(struct tty_struct *); /* 写缓冲区数据大小 */
int (*ioctl)(struct tty_struct *, unsigned int, unsigned long);
long (*compat_ioctl)(struct tty_struct *, unsigned int, unsigned long);
void (*set_termios)(struct tty_struct *, struct ktermios *); /* termios设置函数 */
void (*throttle)(struct tty_struct *); /* 流控,当读缓冲区满时被调用,告知tty驱动不再接收数据 */
void (*unthrottle)(struct tty_struct *);/* 流控,当读缓冲区空时被调用,告知tty驱动可以接收数据 */
void (*stop)(struct tty_struct *); /* 流控,告知tty驱动开始发送数据 */
void (*start)(struct tty_struct *); /* 流控,告知tty驱动停止发送数据 */
void (*hangup)(struct tty_struct *); /* 挂起tty设备 */
int (*break_ctl)(struct tty_struct *, int state); /* 线路规程BREAK控制函数 */
void (*flush_buffer)(struct tty_struct *); /* 清空写缓冲区并丢弃数据 */
void (*set_ldisc)(struct tty_struct *); /* 线路规程设置函数 */
void (*wait_until_sent)(struct tty_struct *, int); /* 同flush_chars
*/
void (*send_xchar)(struct tty_struct *, char); /* 高优先级字符XON/XOFF发送函数 */
int (*tiocmget)(struct tty_struct *); /* 获取当前tty设备线路规程设置 */
int (*tiocmset)(struct tty_struct *, unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *, struct winsize *ws);
int (*set_termiox)(struct tty_struct *, struct termiox *tnew);
int (*get_icount)(struct tty_struct *, struct serial_icounter_struct *icount);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct tty_driver *, int line, char *options);
int (*poll_get_char)(struct tty_driver *, int line);
void (*poll_put_char)(struct tty_driver *, int line, char ch);
#endif
const struct file_operations *proc_fops; /* /proc文件系统操作集 */
};
tty_port_operations是tty端口相关操作, 主要是在打开和关闭tty设备(端口)时使用
struct tty_port_operations {
int (*carrier_raised)(struct tty_port *); /* 端口载体检测 */
void (*dtr_rts)(struct tty_port *port, int raise); /* 控制DTR/RTS*/
void (*shutdown)(struct tty_port *); /* 端口关闭 */
int (*activate)(struct tty_port *, struct tty_struct *); /* 端口激活 */
void (*destruct)(struct tty_port *); /* 端口释放 */
};
5. TTY驱动编写
tty驱动的编写主要步骤如下:
1. 实现tty设备有关的文件操作集(tty_operations)
2. 调用tty_alloc_driver分配一个tty驱动, 并设置driver中相关字段(包括tty_operations变量)
3. 调用tty_register_driver注册tty驱动
4. 如果需要动态注册tty设备, 则需调用tty_register_device或者tty_register_device_attr注册tty设备
5. 接收到数据时, 调用tty_insert_flip_string或者tty_insert_flip_char将数据交给TTY core; TTY core需要发送数据时, 会调用driver提供的回调函数, 在那里面访问硬件送出数据即可
参考:
<Linux字符设备驱动>
<Linux TTY框架子系统>
<Chapter 18. TTY Drivers>
Linux TTY介绍的更多相关文章
- Linux TTY框架【转】
本文转载自:http://ju.outofmemory.cn/entry/281168 1. 前言 由于串口的缘故,TTY是Linux系统中最普遍的一类设备,稍微了解Linux系统的同学,对它都不陌生 ...
- Linux 系统目录介绍
bin : bin 是Binary 二进制的缩写,就是可执行文件了.Bin目录下是用户常用的命令. sbin: 此目录下也是二进制文件 ,不过这里的命令是 超级用户如 root 这样的用户使用的. e ...
- Linux TTY函数跟踪
1. 介绍 本文介绍了TTY打开.TTY读和TTY写操作的函数跟踪过程 2. 示例 下面是一个简单的Linux TTY打开和读写过程 #include <termios.h> #inclu ...
- 01 Linux入门介绍
一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以及游戏支持度不足 L ...
- Linux Epoll介绍和程序实例
Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...
- Linux入门介绍
Linux入门介绍 一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以 ...
- 记录linux tty的一次软锁排查2
在复现tty的死锁问题的时候,文洋兄使用了如下的方式: #include <fcntl.h> #include <unistd.h> #include <stdio.h& ...
- Linux TTY驱动--Serial Core层【转】
转自:http://blog.csdn.net/sharecode/article/details/9197567 版权声明:本文为博主原创文章,未经博主允许不得转载. 接上一节: Linux TTY ...
- Linux命令介绍
资料链接:(Linux基本命令介绍)http://note.youdao.com/share/?id=36c07917f8d3e6437c1e764c3516a3f2&type=note#/ ...
随机推荐
- MySQL Community Server 8.0.16
1 首先 我们需要先下载一个 Mysql 点击这个网址进入 Mysql 的官网的下载地址: https://dev.mysql.com/downloads/mysql/ 首先 根据你的电脑的操作系统选 ...
- 【转帖】LSM树 和 TSM存储引擎 简介
LSM树 和 TSM存储引擎 简介 2019-03-08 11:45:23 长烟慢慢 阅读数 461 收藏 更多 分类专栏: 时序数据库 版权声明:本文为博主原创文章,遵循CC 4.0 BY-S ...
- Appium移动端测试--基础预热
目录 Android自动化环境准备 需要安装的软件: Appium多端架构与自动化 Android自动化前提依赖: 获取App的信息: Android常用命令 adb shell 常用命令列表: An ...
- C语言提高内容目录
(1)基础 数据类型和变量 内存四区(栈 堆 全局 代码区) (2)指针和字符串操作 指针强化 字符串的基本操作 字符串一级指针内存模型图 字符串做为函数参数 (3)二级指针多级指针 二级指针的输入输 ...
- ABP(ASP.NET Boilerplate Project)学习总结
ABP(ASP.NET Boilerplate Project),现下比较流行的一种web框架,因为公司新项目准备使用这种框架,所以写下这篇文章记录下自己一步一步搭建的过程,就当做是对学习的一个总结与 ...
- 拓展 - WebRTC 多视频网络拓扑之三种架构
众所周知,WebRTC非常适合点对点(即一对一)的音视频会话.然而,当我们的客户要求超越一对一,即一对多.多对一设置多对多的解决方案或者服务,那么问题就来了:“我们应该采用什么样的架构?” .简单的呢 ...
- javascript ~~ 符号是什么意思呢?
~ bitwise NOT 运算符 ~对操作数按位取反,两个的意思即作两次取反操作,其实是等作原数本身(操作数是32整数范围内) ~~(Math.random()*7) 即 var n = Math. ...
- 用 node.js 模仿 Apache 的部分功能
首先,这个例子用到了服务端渲染的技术.服务端渲染,说白了就是在服务端使用模板引擎,这里我先简单的介绍一下服务端渲染与客户端渲染之间的区别. 服务端渲染与客户端渲染之间的区别: 客户端渲染不利于搜索引擎 ...
- JavaScript之控制标签属性
var pic=document.getElementById('pic'); var obtn=document.getElementById('btn'); console.log(pic.get ...
- nepenthes用法
安装 # apt-get install nepenthes 配置文件 # vi submit-file.conf submit-file { path "/var/lib/nepenthe ...