SVR4/4.3BSD与Linux对待伪终端的不同方式

2023-11-06

打开伪终端意味着打开了一个“终端对”,这个终端对的其中一个是主终端,另一个是从终端,简单说主终端和类似sshd,telnetd等用户空间的远程协议处理进程连接,而从终端则和shell之类的实际进程连接,在处理远程登录的时候,一般都是由远程协议处理进程打开主终端和从终端,然后就在远程网络终端和本机shell之间建立了一条双向通道--“远程网络终端-(套接字)--本机协议处理进程--主终端--从终端--shell”,在这个“打开主从终端建立连接”的语义以及其实现上,有着不同的标准,总的来说有三种方式,分别是SVR4的方式,BSD的方式以及linux的方式,在“建立连接”的语义上SVR4的方式使用“流”来建立这条连接,而BSD和linux则是自动建立的,在“打开主从终端”的语义上,SVR4和linux是自动确定主终端并打开主终端后自动确定从终端,而BSD则必须手工确定和打开主终端,可见linux处理伪终端的方式是结合SVR4和BSD两种UNIX标准的结果,linux不仅实现这种有意义的最佳组合,而且分别实现了SRV和BSD的两种方式的接口,如果编译CONFIG_LEGACY_PTYS宏,则可以使用BSD的方式,如果编译CONFIG_UNIX98_PTYS,则实现SRV4的接口。
     参见《unix环境高级编程》的第19章,在用户空间,SVR4的方式为:
int ptym_open(char *pts_name)
{
    strcpy(pts_name, "/dev/ptmx"); //准备主终端的文件名字
    fdm = open(pts_name, O_RDWR);  //打开主终端
    grantpt(fdm);  //连接次终端
    ptr = ptsname(fdm); //得到次终端的文件名字
    strcpy(pts_name, ptr);
    return fdm;
}
int ptys_open(int fdm, char *pts_name)
{
    fds = open(pts_name, O_RDWR); //打开次终端
    ioctl(fds, I_PUSH, "ptem");   //压入一个伪终端虚拟模块
    ioctl(fds, I_PUSH, "ldterm"); //压入行规程模块
    return fds;
}
而BSD的方式却是:
int ptym_open(char *pts_name)
{
    ...
    //一个for循环,从/dev/ptyp0开始一直找到/dev/ptyTf为止,寻到第一个没有被使用的作为主终端,然后将对应的文件名的ptyXY中的p改为t,即ttyXY就是次终端
    pts_name[5] = 't';  ///dev/ptyXY中的第6个元素就是p,现在改为t
    return fdm;
}
int ptys_open(int fdm, char *pts_name)
{
    fds = open(pts_name, O_RDWR); //打开次终端
    //无需压入流模块,因为对于bsd来讲,其驱动程序是基于硬编码和clist的。
}
可见SRV4和BSD的方式根本不同,通过理解这种不同,我们也能更加明白/dev目录下的关于终端文件的命名规则了。关于流机制,BSD没有实现,可能是由于BSD自最初就没有SRV经过良好的规划吧,加之流的作者直接贡献流机制于SRV,那时unix已经分裂了。从终端的行规程以及任何终端的行规程之类其实也是一种协议,只是该协议主要规定人-机界面的规则,之所以将之称为行规程就是因为该规程在标准模式下限制了一次输入的结束就是一个换行符,这就是一个行规成协议,毕竟机器并不知道何时人们会输入完毕也就不能预先读取特定大小的数据块,而只能硬性规定一个特殊的字符作为输入结束,该特殊字符就是换行,类似tcp/ip协议,只是没有后者普遍罢了,使用压入流的方式,你可以轻易的堆积一个协议栈,只要将/dev/ip|udp|tcp等协议设备文件依次用ioctl的I_PUSH命令堆积即可。行规程压入了从终端并没有压入主终端,可见主终端仅仅起到一个数据中转的作用,主终端之所以不需要行规程,那是因为从终端可以处理直接从进程写入的数据或者说可以处理数据边界以及转义问题,这是无关紧要的,完全可以重新实现一个伪终端驱动,然后在主从终端都压入行规程模块,这样就显得更加对称了,正如linux后来实现的那样,虽然linux并没有显式地实现流机制,在linux中的伪终端tty的write函数中,主从终端都是统一一致的,如此一来在linux中,主从终端就更加像一对管道了,起码比SRV的要对称:
static int pty_write(...)
{
    struct tty_struct *to = tty->link; //直接获取“一对的另一半”
    ...
    to->ldisc.receive_buf(to, temp_buffer, NULL, n);//将数据放入另一半的缓冲区
    ...
}
如果看一下linux实现pty的源码,就会发现实际上pts使用的行规程是tty_ldisc_N_TTY,其receive_buf对数据其实并没有做太复杂的加工,因此这条管道并不复杂。
     在打开一对终端方面,linux实现了两种方式,SRV4的方式和BSD的方式,总之,实现这两种接口是之前unix标准混战的结果。以SRV4的方式为例,linux中使用了一个/dev/ptmx设备文件,该设备文件只有一个却可以集中代表所有的主终端,任何sshd,telnetd之类的进程都可以只使用者一个终端设备文件,虽然设备文件是一个,但是由于内核中file数据结构是基于进程的,因此各个进程对该设备文件的引用却可以容纳不同的数据,包括不同的从终端。在ptmx_open中,不仅系统可以自动分配一个主终端,而且还为该主终端绑定了一个从终端,主终端设置到file结构体的private_data字段上,之后诸如sshd,telnetd之类的进程读写/dev/ptmx文件时,虽然它们读写的是同一个文件,可是由于file结构体不再它们之间共享,因此它们取到的file->private_data也就不同了:
static int ptmx_open(struct inode * inode, struct file * filp)
{
    struct tty_struct *tty;
    int index;
    idr_pre_get(&allocated_ptys, GFP_KERNEL); //和BSD的实现不同,SRV的方式在内核中查找可用的主终端
    idr_ret = idr_get_new(&allocated_ptys, NULL, &index); //得到新的项
    ...
    retval = init_dev(ptm_driver, index, &tty);//此中实现终端对的相互link
    filp->private_data = tty; //重要的赋值
    devpts_pty_new(tty->link); //linux中的从终端设备文件是动态生成和删除的,因此linux使用了其强大的VFS机制,通过实现一个pts文件系统来支持这种动态的增删。
    ...
}
SRV4使用伪终端的方式是一对多的,ptmx集合了所有的主终端,同一个文件在不同的进程空间做区分,而BSD的方式却直接将多个一对一的终端对开放给接口调用者。在init_dev中,最为重要的一段是:
tty = alloc_tty_struct(); //分配主终端
initialize_tty_struct(tty); //初始化主终端的线路规程之类,包括些许工作队列
tty->driver = driver; //指定driver
tty->index = idx;
...
o_tty = alloc_tty_struct(); //分配从终端
initialize_tty_struct(o_tty); //初始化主终端的线路规程之类
o_tty->driver = driver->other; //pty_init的时候,主从driver都将other设置为对方
o_tty->index = idx;
tty_line_name(driver->other, idx, o_tty->name);
...
tty->link   = o_tty; //连接彼此,从此以后两个终端一主一从构成一个包含很多控制功能的管道
o_tty->link = tty;
linux的sshd等进程在调用完/dev/ptmx的open之后,实际上一对终端就建立起来了,由于没有实现SRV4的流机制,因此不需要在用ioctl将更多的协议模块PUSH上去了,这个过程直接在init_dev中就做了,可以猜想一切基于SRV4标准实现的OS,其ptmx的open要简单的多,因此init_dev简单得多,不需要系统做任何事,后续的所有工作几乎全靠调用者来用ioctl完成,比linux更灵活,但是对于很多凡人来讲也更繁琐。
     十分欣赏SRV的那种I_PUSH实现的将ip,icmp,udp等堆积成协议栈的方式--流机制,同时更乐于使用linux这种将一切都做好,但是还可以定制的OS。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SVR4/4.3BSD与Linux对待伪终端的不同方式 的相关文章

  • 如何从远程 ssh 连接上运行的 tmux(复制模式)复制到本地剪贴板

    我通过 OS X 上的 VirtualBox 运行 Linux 我通过在无头状态下运行虚拟机 然后使用端口转发 sshing 到 Linux 机器来实现这一点 现在 无论复制到我的虚拟机上的剪贴板 我都可以粘贴到我的远程 ssh 会话上 但
  • 将node.js +expressjs应用程序的NODE_ENV设置为ubuntu下的守护进程

    我按照这些说明让守护进程正常工作 http kevin vanzonneveld net techblog article run nodejs as a service on ubuntu karmic http kevin vanzon
  • XAMPP Windows 上的 Php Cron 作业

    嗯 我是这个词的新手CRON 据我所知 这是一个Unix安排特定操作在定义的时间间隔后执行的概念 我需要运行一个php文件 每小时更新一次数据库 但我的困惑在于安排执行 我在用XAMPP用于 Windows 7 上的本地开发测试 我发现了什
  • 返回具有关联类型的特征

    struct A struct PropA struct B struct PropB trait AB type prop fn a self gt fn b self p Self prop gt impl AB for A type
  • 使用 ProcessBuilder 运行 shell 脚本

    我正在尝试使用 Java 和 ProcessBuilder 运行脚本 当我尝试运行时 我收到以下消息 error 2 没有这样的文件或目录 我不知道我做错了什么 但这是我的代码 ps 我尝试只执行不带参数的脚本 错误是相同的 String
  • 计算 TCP 重传次数

    我想知道在LINUX中是否有一种方法可以计算一个流中发生的TCP重传的次数 无论是在客户端还是服务器端 好像netstat s解决了我的目的
  • 如何反汇编、修改然后重新组装 Linux 可执行文件?

    无论如何 这可以做到吗 我使用过 objdump 但它不会产生我所知道的任何汇编器都可以接受的汇编输出 我希望能够更改可执行文件中的指令 然后对其进行测试 我认为没有任何可靠的方法可以做到这一点 机器代码格式非常复杂 比汇编文件还要复杂 实
  • 如何对结构切片而不是切片结构进行范围调整

    稍微玩了一下 Go HTML 模板后 我发现的所有循环模板中对象的示例都是将切片结构传递给模板 有点像这个示例 type UserList struct Id int Name string var templates template M
  • 在 Swift 中使用 NSCoding 归档可选结构数组?

    我已经在 Obj C 中完成了大量 NSCoding 归档 但我不确定它如何处理 Swift 中的结构 也不确定它如何处理具有可选值的数组 这是我的代码 public struct SquareCoords var x Int y Int
  • 如何在linux中使用iptables将http和https流量转发到透明代理[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 这个问题似乎不是关于主要由程序员使用的特定编程问题 软件算法或软件工具 help on topic 如果您认为该问题与主题相关另一个 St
  • Grep 递归和计数

    需要在具有大量子目录的目录中搜索文件内的字符串 我在用着 grep c r string here 我怎样才能找到总数量 如何仅输出至少具有一个实例的文件 使用 Bash 的进程替换 这给出了我认为是您想要的输出 如果不是 请澄清问题 gr
  • 套接字发送调用被阻塞很长时间

    我每 10 秒在套接字上发送 2 个字节的应用程序数据 阻塞 但发送调用在下面的最后一个实例中被阻塞超过 40 秒 2012 06 13 12 02 46 653417 信息 发送前 2012 06 13 12 02 46 653457 信
  • 用于获取特定用户 ID 和进程数的 Bash 脚本

    我需要 bash 脚本来计算特定用户或所有用户的进程 我们可以输入 0 1 或更多参数 例如 myScript sh root deamon 应该像这样执行 root 92 deamon 8 2 users has total proces
  • 在嵌入式系统上将内核控制台发送到哪里?

    我正在开发一个嵌入式系统 该系统当前通过串行端口 1 上的控制台输出启动 Linux 使用启动加载程序中的控制台启动参数 然而 最终我们将使用这个串行端口 内核控制台输出的最佳解决方案是什么 dev null 能否以某种方式将其放在 pty
  • std::bind2nd 和 std::bind 与二维数组和结构数组

    我知道 C 有 lambda 并且 std bind1st std bind2nd 和 std bind 已弃用 然而 从C 的基础开始 我们可以更好地理解新特性 所以 我从这个非常简单的代码开始 使用int 数组s 第一个例子 与std
  • #*/ 在 UNIX Shell 脚本中使用

    谁能详细说明 在 UNIX Shell 脚本中的工作原理 我已经看到它在 Korn Shell 中的使用 它专门用于删除文件的扩展名 例如 func write app log o 删除状态文件 CIE STATUS FILE 这里假设文件
  • 在用户程序中使用 或在驱动程序模块代码中使用 ...这有关系吗?

    我正在开发一个设备驱动程序模块和关联的用户库来处理ioctl 来电 该库获取相关信息并将其放入一个结构中 该结构被传递到驱动程序模块中并在那里解压 然后进行处理 我省略了很多步骤 但这就是总体思路 一些数据通过结构体传递ioctl is u
  • 亚马逊 Linux - 安装 openjdk-debuginfo?

    我试图使用jstack在 ec2 实例上amazon linux 所以我安装了openjdk devel包裹 sudo yum install java 1 7 0 openjdk devel x86 64 但是 jstack 引发了异常j
  • Apache LOG:子进程 pid xxxx 退出信号分段错误 (11)

    Apache PHP Mysql Linux 注意 子进程 pid 23145 退出信号分段错误 11 tmp 中可能存在 coredump 但 tmp下没有找到任何东西 我怎样才能找到错误 PHP 代码中函数的无限循环导致了此错误
  • 在 foreach 循环中更改另一个结构内的结构

    打印以下代码 调用 MyMethod 时 0 0 0 1 我希望它打印 0 0 1 1 为什么是这样 Code private struct MyStruct public MyInnerStruct innerStruct private

随机推荐

  • 以树状结构显示文件夹

    以树状结构显示文件夹 题目要求 编写程序 在命令行中以树状结构显示特定的文件夹及其子文件夹 如果子文件是文件则需要显示文件大小 最后统计整个目录的大小 public class Test static long fileNum 0 stat
  • 华为OD机试 C++ 报数问题

    题目 你和你的朋友们围成一个圈玩游戏 从第一个人开始 依次报数 1 2 3 当数到3的时候 那个人就得退出游戏 然后从他的下一个朋友继续开始 1 2 3 同样的 数到3的人又得退出 这样一直进行下去 直到圈里只剩下一个人 人会是谁 任务 给
  • Android Studio更新升级方法

    android studio一直在更新完善 为了与时俱进 我们当然要将工具更新到最新版本啦 其实更新本来是很简单 只要从Android Studio Help菜单中选择Check for Update即可 但因为 伟大的墙 实际更新失败 下
  • JS浏览器调试:Browser对象

    好久不搞前端 最近在搞钉钉的第三方应用的时候 前端给我露了一手 涉及到知识主要是JavaScript的Browser对象 首先现在很多PC客户端现在技术选型选择会选择nodejs这类 像nw js或者Electron 都会涉及到chromi
  • 给input框赋值成功后input框不能进行编辑

    选中一项后 input框不能进行编辑
  • 【工具】(z-library平替)Clibrary中文图书馆,电子书大全

    目录 新版目录 更新于2023 7 11 一 更新时间和简介 二 步骤 老版目录 接口无法使用 1 z library和Clibrary简介 2 Clibrary网址 3 具体操作界面 新版目录 更新于2023 7 11 一 更新时间和简介
  • three.js 制作地球标注的两种方法

    一 Sprite精灵标签 效果图 精灵标签 标签永远面向相机 function createTxt position name var texture new THREE CanvasTexture getCanvasFont var fo
  • 自学——一个月入门前端④

    本文的内容有关css盒模型 盒模型 在css中 所有的元素都被一个个的 盒子 包围着 广泛的使用两种盒子 块级盒子和内联盒子 这两种盒子会在页面流和元素之间的关系 block 绝大部分情况下盒子会和父容器一样宽 每个盒子都会换行 width
  • C/C++判断指针是否为空

    if p NULL if NULL p if p nullptr if nullptr p if p 0 if 0 p if p false if false p if p 个人理解 如有不对 敬请指正 最佳判断是if p 因为 p是一个指
  • 02-分布式协调服务ZooKeeper

    目录 一 ZooKeeper简介 1 什么是Zookeeper 2 基本特性 二 ZooKeeper原理 1 系统架构 1 1 角色分工 1 2 设计思想 1 3 为什么要引入Observer ZK 3 3 0 2 数据模型 2 1 Zno
  • 花书+吴恩达深度学习(二六)近似推断(EM, 变分推断)

    文章目录 0 前言 1 将推断视为优化问题 1 1 期望最大化 EM 1 2 最大后验推断 MAP 1 3 稀疏编码 2 变分推断 2 1 离散型潜变量 2 2 连续性潜变量 如果这篇文章对你有一点小小的帮助 请给个关注 点个赞喔 我会非常
  • 【Redis】持久化之AOF

    Redis持久化之AOF 以日志的形式来记录每个写操作 增量保存 将Redis执行过的所有写指令记录下来 读操作不记录 只许追加文件但不可以改写文件 redis启动之初会读取该文件重新构建数据 换言之 redis 重启的话就根据日志文件的内
  • 【element】之el-popconfirm:弹出组件确认按钮绑定

    element之el popconfirm 弹出组件确认按钮绑定 一 element官网说明 element中el popconfirm实例 element提供的接口 二 实际代码说明 无用代码 正确代码 一 element官网说明 ele
  • 通过bilibili_api获取bilibili弹幕+绘制词云的方法!

    由于自己学艺不精 后续词云的简略代码没怎么看懂 梳理了一遍把整个的学习内容记录下来 主要参考的为bilibili api的教程和词云的生成教程 https blog csdn net itanders article details 888
  • 华为b6手环能升级鸿蒙吗,华为手环B6全新发布:跨界形态再升级 强劲性能革新穿戴体验...

    2020年7月30日 北京 今日 华为正式推出了融合耳机和手环形态的跨界穿戴智能手环华为手环B6 它采用1 53英寸高清3D弧面柔性屏 外观时尚 操控自如 独创的分离式设计 让这款产品兼具蓝牙耳机的舒适佩戴和华为智能手环的专业运动健康功能
  • [Ubuntu] [Qt] Ubuntu18.04.6安装Qt后打不开

    1 安装完Qt5 15 2后点击图标没反应 2 通过指令打开Qt 可以看到失败的原因是因为glibc 2 28没找到 Qt Tools QtCreator bin qtcreator software Qt Tools QtCreator
  • labelimg 修正模型错误标注遇到的问题

    场景介绍 使用了 模型 如YOLOv5 v7 detec py 保存的 YOLO 格式的结果 包括测试的图像和对应的 txt 文件 模型跑出来的结果可能不够准确 需要手工修正下 因此我需要使用 labelimg 来可视化图像 并且修改其标注
  • Tomcat安装版和解压版

    在eclipse中开发web项目经常需要在eclipse中添加tomcat服务器 之前下载了目前最新版本tomcat9的zip版 由于目前的eclipse只支持到tomcat8 原本准备就这样安装了 在浏览器输入了n次http localh
  • redis持久化操作RDB和AOF详解与操作(docker)

    redis持久化 Redis 提供了两种不同的持久化方法来将数据存储到硬盘里面 一种方法叫快照 snapshotting RDB 它可以将存在于某一时刻的所有数据都写入硬盘里面 另一种方法叫只追加文件 append only file AO
  • SVR4/4.3BSD与Linux对待伪终端的不同方式

    打开伪终端意味着打开了一个 终端对 这个终端对的其中一个是主终端 另一个是从终端 简单说主终端和类似sshd telnetd等用户空间的远程协议处理进程连接 而从终端则和shell之类的实际进程连接 在处理远程登录的时候 一般都是由远程协议