上一篇<<linux终端设备:tty子系统相关的初始化>>分析了tty子系统、tty设备、console设备的初始化及创建过程。默认情况console终端的启动程序为agetty,它访问的是/dev/console inode,执行的console_fops操作结构中的函数,而由类Xwindow中的终端或远程ssh访问的是/dev/ptmx (pty(伪终端)),执行的是ptmx_fops操作结构中的函数,它实际还是tty设备(还是执行tty_fops,只是open改成了ptmx_open)。ptm(/dev/ptmx)实际上只是用户层创建pty设备的桥梁,通过tty_fops中的函数,完成pty设备的创建、通信等工作,并且pty设备以/dev/pts/* inode的形式展现在用户层(基于devpts文件系统)。

目录

1. pty

1.1 pty_init

2. 源码结构

3. 扩展函数/变量

目录预览

1. pty

1.1 pty_init

pty伪终端目前默认采用UNIX98系列,也就是指定的CONFIG_UNIX98_PTYS编译选项,执行unix98_pty_init函数
  pty设备采用主从模式(master -> slave),并由/dev/ptmx做外层访问(与内部做了隔离),用户层通过访问/dev/ptmx生成对应的/dev/pts/* inode,数据读写这部分采用行规程刷新,挂起事件、SAK等事件依赖于input事件子系统上报的按键组合,通过唤醒对应的队列完成触发对应的事件函数

static int __init pty_init(void)
{legacy_pty_init(); // CONFIG_LEGACY_PTYS选项默认没有打开,本函数为空 (分配/注册pty_driver驱动(主)和pty_slave_driver驱动(从),pty_driver的other指定pty_slave_driver,也就是由pty_driver遍历控制pty_slave_driver驱动)unix98_pty_init(); // CONFIG_UNIX98_PTYS=y,执行这个函数
||
\/
static void __init unix98_pty_init(void)
{ptm_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,TTY_DRIVER_RESET_TERMIOS |TTY_DRIVER_REAL_RAW |TTY_DRIVER_DYNAMIC_DEV |TTY_DRIVER_DEVPTS_MEM |TTY_DRIVER_DYNAMIC_ALLOC); // 分配tty驱动,ptm_driver (master)// TTY_DRIVER_RESET_TERMIOS 0x0002 请求tty层在最后一个进程关闭设备后重置termios设置// TTY_DRIVER_REAL_RAW 0x0004 驱动程序将保证不设置任何特殊字符处理标志/* TTY_DRIVER_DYNAMIC_DEV 0x0008 * 当设备在系统中找到时,需要通过对tty_register_device()的调用来注册tty设备,* 并通过对tty_unregister_device()的调用来取消注册tty设备,这样设备就会正确地显示在sysfs中。* 如果没有设置所有的&tty_driver,当调用tty_register_driver()时,将由sysfs中的tty核心创建Num项。* 当主tty驱动程序向tty核心注册时,具有tty设备的驱动程序可以显示和消失。*//* TTY_DRIVER_DEVPTS_MEM 0x0010 * 不要使用标准数组(&tty_driver.ttys and &tty_driver.termios)* 而是使用通过devpts文件系统输入的动态内存。这只适用于pty驱动程序*/ // TTY_DRIVER_DYNAMIC_ALLOC 0x0040 不要为这个驱动程序(&tty_driver.ports)分配每行需要的结构,因为这会浪费内存pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,TTY_DRIVER_RESET_TERMIOS |TTY_DRIVER_REAL_RAW |TTY_DRIVER_DYNAMIC_DEV |TTY_DRIVER_DEVPTS_MEM |TTY_DRIVER_DYNAMIC_ALLOC); // 分配tty驱动,pts_driver (slave)ptm_driver->driver_name = "pty_master";  // master驱动赋值ptm_driver->name = "ptm";ptm_driver->major = UNIX98_PTY_MASTER_MAJOR;ptm_driver->minor_start = 0;ptm_driver->type = TTY_DRIVER_TYPE_PTY;ptm_driver->subtype = PTY_TYPE_MASTER;ptm_driver->init_termios = tty_std_termios; // 非同步通信端口配置ptm_driver->init_termios.c_iflag = 0;ptm_driver->init_termios.c_oflag = 0;ptm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;ptm_driver->init_termios.c_lflag = 0;ptm_driver->init_termios.c_ispeed = 38400;ptm_driver->init_termios.c_ospeed = 38400;ptm_driver->other = pts_driver; // other指定pts_driver驱动tty_set_operations(ptm_driver, &ptm_unix98_ops); // 链接ptm_unix98_ops操作结构

tty_std_termios
ptm_unix98_ops

     pts_driver->driver_name = "pty_slave"; // slave驱动赋值pts_driver->name = "pts";pts_driver->major = UNIX98_PTY_SLAVE_MAJOR;pts_driver->minor_start = 0;pts_driver->type = TTY_DRIVER_TYPE_PTY;pts_driver->subtype = PTY_TYPE_SLAVE;pts_driver->init_termios = tty_std_termios;pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;pts_driver->init_termios.c_ispeed = 38400;pts_driver->init_termios.c_ospeed = 38400;pts_driver->other = ptm_driver; // other指定ptm_driver驱动tty_set_operations(pts_driver, &pty_unix98_ops); // 链接pty_unix98_ops操作结构

pty_unix98_ops

if (tty_register_driver(ptm_driver)) // 注册tty设备 (ptm_driver),指定tty_classpanic("Couldn't register Unix98 ptm driver");if (tty_register_driver(pts_driver)) // 注册tty设备 (pts_driver),指定tty_classpanic("Couldn't register Unix98 pts driver");/* Now create the /dev/ptmx special device */tty_default_fops(&ptmx_fops); // ptmx_fops = tty_fops; 使用tty_fops作为默认操作ptmx_fops.open = ptmx_open;

ptmx_open

cdev_init(&ptmx_cdev, &ptmx_fops);  // 初始化ptmx_cdev设备,关联ptmx_fops结构if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) // 注册ptmx_cdev设备panic("Couldn't register /dev/ptmx driver");device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); // 创建ptmx_cdev设备,指向tty_class
}

2. 源码结构

tty_std_termios 非同步通信端口配置

struct ktermios tty_std_termios = {     /* for the benefit of tty drivers  */.c_iflag = ICRNL | IXON,.c_oflag = OPOST | ONLCR,.c_cflag = B38400 | CS8 | CREAD | HUPCL,.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |ECHOCTL | ECHOKE | IEXTEN,.c_cc = INIT_C_CC,.c_ispeed = 38400,.c_ospeed = 38400,/* .c_line = N_TTY, */
};
EXPORT_SYMBOL(tty_std_termios);

ptm_unix98_ops pty主端操作结构

/* pty的主端可以执行TIOCSPTLCK,因此有pty_unix98_ioctl */
static const struct tty_operations ptm_unix98_ops = {.lookup = ptm_unix98_lookup,.install = pty_unix98_install,.remove = pty_unix98_remove,.open = pty_open,.close = pty_close,.write = pty_write,.write_room = pty_write_room,.flush_buffer = pty_flush_buffer,.unthrottle = pty_unthrottle,.ioctl = pty_unix98_ioctl,.compat_ioctl = pty_unix98_compat_ioctl,.resize = pty_resize,.cleanup = pty_cleanup,.show_fdinfo = pty_show_fdinfo,
};

pty_unix98_ops pty从端操作结构

static const struct tty_operations pty_unix98_ops = {.lookup = pts_unix98_lookup,.install = pty_unix98_install,.remove = pty_unix98_remove,.open = pty_open,.close = pty_close,.write = pty_write,.write_room = pty_write_room,.flush_buffer = pty_flush_buffer,.unthrottle = pty_unthrottle,.set_termios = pty_set_termios,.start = pty_start,.stop = pty_stop,.cleanup = pty_cleanup,
};

3. 扩展函数/变量

ptmx_open 从ptmx驱动程序分配一个主pty设备

/** @inode:设备文件的inode* @filp: tty的文件指针*/
static int ptmx_open(struct inode *inode, struct file *filp)
{struct pts_fs_info *fsi;struct tty_struct *tty;struct dentry *dentry;int retval;int index;nonseekable_open(inode, filp); // 移除FMODE_LSEEK、FMODE_PREAD 、FMODE_PWRITE标志// filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);/* 拒绝ptmx上的fsnotify事件,因为它是一个共享资源 */filp->f_mode |= FMODE_NONOTIFY;  // 增加FMODE_NONOTIFY标志retval = tty_alloc_file(filp); // 为file->private_data分配内存 (struct tty_file_private *priv)if (retval)return retval;fsi = devpts_acquire(filp); // 获取devpts文件系统结构

devpts_acquire

mutex_lock(&devpts_mutex);
index = devpts_new_index(fsi); // 分配一个未使用的索引值(ID)
mutex_unlock(&devpts_mutex); mutex_lock(&tty_mutex);
tty = tty_init_dev(ptm_driver, index); // 分配tty_struct对象,为其设置行规程,初始化挂起事件、SAK事件队列,各种锁、队列初始化等等
/*这里返回的tty是锁定的,所以我们可以安全地删除互斥量*/
mutex_unlock(&tty_mutex);

tty_init_dev

set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
tty->driver_data = fsi;tty_add_file(tty, filp); // 将新文件与tty结构关联
dentry = devpts_pty_new(fsi, index, tty->link); // 创建inode到/dev/pts/*  (fsi)tty->link->driver_data = dentry;
retval = ptm_driver->ops->open(tty, filp); // 检查tty标志/设置标志
}

devpts_acquire 获取devpts文件系统结构

struct pts_fs_info *devpts_acquire(struct file *filp)
{       struct pts_fs_info *result;struct path path;struct super_block *sb;path = filp->f_path; // 文件所在的路径path_get(&path); // 获取路径的引用,增加dentry和vfsmount的引用计数/* 检查我们是否已经找到了合适的devpts文件系统 */if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) {  // DEVPTS_SUPER_MAGIC  devpts文件系统标志int err;err = devpts_ptmx_path(&path); // 为0表示有合适的devpts文件系统if (err) {result = ERR_PTR(err);goto out;}}/** pty代码需要保存额外的引用以防最后一次/dev/tty关闭*/sb = path.mnt->mnt_sb;atomic_inc(&sb->s_active);result = DEVPTS_SB(sb); // 如果文件系统private info存在,表示可用...
}

tty_init_dev 分配tty_struct对象,为其设置行规程,初始化挂起事件、SAK事件队列,各种锁、队列初始化等等

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{struct tty_struct *tty;int retval;if (!try_module_get(driver->owner)) // 检查模块是否存在,并且引用计数不为0return ERR_PTR(-ENODEV);tty = alloc_tty_struct(driver, idx); // 分配tty_struct对象,为其设置行规程,初始化挂起事件、SAK事件工作队列,ops等等

alloc_tty_struct

tty_lock(tty); // ... mutex_lock(&tty->legacy_mutex);
retval = tty_driver_install_tty(driver, tty); // tty保存到驱动的tty数组列表中if (!tty->port)tty->port = driver->ports[idx];retval = tty_ldisc_lock(tty, 5 * HZ); // 唤醒行规程的读写,睡醒指定时间后获取信号量semtty->port->itty = tty;retval = tty_ldisc_setup(tty, tty->link); // 设置行规程并将其绑定到tty
tty_ldisc_unlock(tty); // 清理TTY_LDISC_HALTED、TTY_LDISC_CHANGING标志,释放信号量/* Return the tty locked so that it cannot vanish under the caller */return tty;
}

alloc_tty_struct 分配tty_struct对象,为其设置行规程,初始化挂起事件、SAK事件工作队列,ops等等

struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
{struct tty_struct *tty;tty = kzalloc(sizeof(*tty), GFP_KERNEL_ACCOUNT); // 与GFP_KERNEL相同,只是分配记入kmemcgif (tty_ldisc_init(tty)) { // 为新分配的tty设置行规程对象kfree(tty);return NULL;}tty->ctrl.session = NULL;tty->ctrl.pgrp = NULL;mutex_init(&tty->legacy_mutex);mutex_init(&tty->throttle_mutex);init_rwsem(&tty->termios_rwsem);mutex_init(&tty->winsize_mutex);init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait);init_waitqueue_head(&tty->read_wait);INIT_WORK(&tty->hangup_work, do_tty_hangup);  // 初始化工作队列,do_tty_hangup 挂起事件的实际处理程序mutex_init(&tty->atomic_write_lock);spin_lock_init(&tty->ctrl.lock);spin_lock_init(&tty->flow.lock);spin_lock_init(&tty->files_lock);INIT_LIST_HEAD(&tty->tty_files);INIT_WORK(&tty->SAK_work, do_SAK_work); // do_SAK_work, SAK组合键执行函数,如alt+k+sysrqtty->driver = driver;tty->ops = driver->ops;tty->index = idx;tty_line_name(driver, idx, tty->name);tty->dev = tty_get_device(tty);

目录预览

<<linux终端设备:tty子系统相关的初始化>>

linux终端设备:pty设备初始化、创建过程相关推荐

  1. Linux网络摄像头设备端调试过程

    转载请标明出处:https://blog.csdn.net/u013752202/article/details/96502576 简介 网络摄像头,即常说的免驱摄像头,其采用UVC协议通过USB跟主 ...

  2. Linux ALSA声卡驱动之四:Control设备的创建

    声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢! Control接口 Control接口主要让用户空间的应用程序(alsa-lib)可以访问 ...

  3. ALSA声卡驱动四之Control设备的创建

    Control接口 Control接口主要让用户空间的应用程序(alsa-lib)可以访问和控制音频codec芯片中的多路开关,滑动控件等.对于Mixer(混音)来说,Control接口显得尤为重要, ...

  4. Linux: 设备节点创建移除过程简析

    文章目录 1. 前言 2. 分析背景 3. 设备节点的创建和移除 3.1 通过 devtmpfs 创建移除设备节点 3.1.1 devtmpfs 初始化 3.1.2 通过 devtmpfs 创建设备节 ...

  5. 《Linux驱动:设备节点文件的创建过程》

    文章目录 一.前言 二.uevent机制 2.1 Sysfs文件系统 2.2 Kobject的事件类型 三.mdev应用程序 3.1 mdev的配置文件 四.实例分析 4.1 uevent机制 4.2 ...

  6. linux终端设备:tty子系统相关的初始化

    linux类系统相较于windos类系统用"魔幻"也不为过,神奇的控制台终端如ctrl+alt+F1至F6.UI桌面打开终端.远程ssh登录等等,在神奇的命令行协助下总能随时随地完 ...

  7. 高通平台msm8953 Linux DTS(Device Tree Source)设备树详解之二(DTS设备树匹配过程)

    本系列导航: 高通平台8953  Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇) 高通平台8953 Linux DTS(Device Tree Source ...

  8. Linux:驱动之自动创建字符设备的设备文件(未完)

    自动创建字符设备的设备文件 目前尚不是最终版本,还望有心人自己学习的时候,把自己整合的知识点相关的答案也好问题也好,或者实践过程中的一些操作截图,再或者其他的一些想要分享材料发给笔者邮箱:uestc_ ...

  9. linux内核静态添加sdio设备,Linux下sdio设备扫描过程

    前言 本文基于Linux version 3.10.52版本代码分析sdio设备的扫描过程,同时选择sdio wifi设备作为分析对象,在分析过程中,附带上sdio的协议内容,帮助初学人员学习sdio ...

最新文章

  1. java反射用在哪里_Java反射
  2. 日本电信企业5G部署计划 限制了华为中兴设备的使用
  3. 面试官:讲讲Spring框架Bean的加载过程
  4. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | 类加载器构造函数分析 | DexPathList 引入 )
  5. SQL Server 查看identity值的几种方法。
  6. 两个重要而又容易被忽视的角色
  7. UI设计干货素材|如何正确使用直观打折数字使画面更饱满更具促销感!
  8. 请问诸位大神,Android怎么实现图片转动
  9. python异常数据处理_python中如何处理异常值
  10. 【Linux】C语言——贪吃蛇
  11. Remoting服务实例
  12. java自动回复_java实现自动回复聊天机器人
  13. h5分享到新浪微博 php,h5分享图文链接到微博如何实现
  14. 基于Java实现大学生求职招聘信息网站系统
  15. java中怎样显示图片_[Java教程]Java中显示图片的方法
  16. 微信红包服务器卡死,东大跨年红包记--并发案例分析
  17. 2021-10-12 Java 中 Filed.modifiers 之 java.lang.reflect.Modifier
  18. python 操作鼠标和键盘
  19. docker-redis
  20. 测试之美(2)对测试的几点理解----谁是利益相关者?

热门文章

  1. 多元一次不定方程解的个数
  2. 多模态的研究现状与应用场景的调查研究
  3. 【计算机组成原理】CPU如何区分指令和数据
  4. C++动态库*.dll文件的Debug/Release版本是否可以混用(交叉用)?
  5. 爬虫实践:爬取搜狗图片
  6. Java的GUI编程---AWT介绍
  7. python中文文本分词_SnowNLP:?中文分词?词性标准?提取文本摘要,?提取文本关键词,?转换成拼音?繁体转简体的 处理中文文本的Python3 类库...
  8. 神都夜行录无法显示服务器,神都夜行录提示无响应怎么办
  9. ArcGIS中的土地利用变化分析
  10. Java基础二维数组