一、知识准备

1、在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件(比如:块设备,socket套接字,pipe队列)

2、操作这些不同的类型就像操作文件一样,比如增删改查等

3、块设备支持随机访问,而字符设备只能依据先后顺序来读取数据。最典型的字符设备就是tty

二、环境准备

组件 版本
OS CentOS Linux release 7.5.1804

三、什么是tty?

根据史料记载:

An ASR33 Teletype - origin of the abbreviation tty.

tty来源一种电传打印机(teletype),就像这样:

● 敲击键盘输入不同的字符,然后由打印机将字符打印在纸上

● 历史不断在往前发展,出现了计算机之后,计算机模拟了teletype的模式:通过外部终端输入,将输入的字符打印在屏幕上

● 在teletype与计算机之间用串口相连,并且在计算机上通过信号转换(模拟信号转换为数字信号),让计算机能够识别,从而操作计算机

● 由于计算机厂商众多,每个厂商都有自己风格的输入设备,所以计算机为了兼容这些设备,开发了内核tty模块


  1. +-----------------+
  2. | |
  3. +--------+ | +-------------+ |
  4. |teletype|-----------------> |serial | |
  5. +--------+ | |communication| |
  6. | +-----+-------+ |
  7. | | |
  8. | v |
  9. | +----------+ | +----------+
  10. | |tty driver| |------->| display |
  11. | +----------+ | +----------+
  12. | |
  13. |computer |
  14. +-----------------+

四、tty设备文件

登陆到操作系统(不使用SSH协议,而使用控制台直接登陆),首先查看当前进程号所使用的tty

  1. [root@localhost ~]# tty
  2. /dev/tty1
  3. [root@localhost ~]# ls -l /dev/tty1
  4. crw--w---- 1 root tty 4, 1 Nov 20 23:24 /dev/tty1

当前所使用的是/dev/tty1,并且tty1也分配了主设备号与次设备号(关于主设备号与次设备号,请看之前的文章:块设备文件)

查看进程打开的描述符

  1. [root@localhost ~]# echo $$
  2. 5598
  3. [root@localhost ~]# ls -l /proc/5598/fd
  4. total 0
  5. lrwx------ 1 root root 64 Nov 19 22:23 0 -> /dev/tty1
  6. lrwx------ 1 root root 64 Nov 19 22:23 1 -> /dev/tty1
  7. lrwx------ 1 root root 64 Nov 19 22:23 2 -> /dev/tty1
  8. lrwx------ 1 root root 64 Nov 19 22:23 255 -> /dev/tty1

进程打开了4个文件描述符,这四个文件描述符都是/dev/tty1,他们的作用分别是:

0:标准输入

1:标准输出

2:标准错误

255:这个比较特殊,主要用于当tty重置的时候对0,1,2的一份复制(个人观点是对tty之前的历史信息作为一份复制)

更多的信息,请拜读大神的书《Shell Scripting: Expert Recipes for Linux, Bash, and more》,这里是链接(大概在267页):

https://doc.lagout.org/operating system /linux/Commands and Shell Programming/Shell Scripting.pdf

五、ssh登陆之后的tty

刚才介绍的都是操作系统提供的控制台登陆之后的情况,如果用ssh服务登陆之后会产生什么情况呢?

首先介绍一个非常重要的概念,伪终端pty:

● pty是一对虚拟的字符设备,提供双向通信。pty一般由master与slave组成

● pty的出现是为了满足现在的登陆需求:网络登陆(ssh登陆、telnet登陆等)、Xwindow等

● 历史上有两套接口标准:分别是BSD与unix98,当前大多数pts都是基于unix98标准来实现的

● unix98的工作流程:

(1)进程对/dev/ptmx调用open(),返回pseudoterminal master(PTM)的文件描述符,并且在/dev/pts下创建pseudoterminal slave(PTS): /dev/pts/0

(2)调用grantpt()修改PTS的文件权限;调用unlockpt()对PTS解锁;最后调用slavename()得到PTS文件名字

(3)此时,PTM与PTS都已经正常打开,并且建立一条通道,两端分别连接PTM与PTS

(4)进程对PTM写的数据可以从PTS读出来,反之亦然

下面重点介绍一下基于unix98实现的sshd pty(主要分为登陆阶段和执行命令阶段):

登陆:

(1)当进程ssh client请求与sshd建立登陆连接的时候,经过TCP握手以及tls握手之后,确认是一个合法的请求,sshd会fork()一个子进程出来专门服务于这条连接

  1. [root@localhost ~]# ps -ef | grep sshd
  2. root 894 1 0 Nov25 ? 00:00:00 /usr/sbin/sshd -D
  3. root 3126 894 0 Nov25 ? 00:00:00 sshd: root@pts/0

(2)子进程3126 /dev/ptmx调用open(),得到PTM的文件描述符以及PTS的文件名

  1. #这里使用strace跟踪sshd主进程和它创建的子进程,然后打开另外一个shell登陆服务器
  2. [root@localhost ~]# strace -p 894 -ff -o sshd
  3. strace: Process 894 attached
  4. strace: Process 3126 attached
  5. strace: Process 3127 attached
  6. strace: Process 3128 attached
  7. strace: Process 3129 attached
  8. strace: Process 3130 attached
  9. strace: Process 3131 attached
  10. strace: Process 3132 attached
  11. strace: Process 3133 attached
  12. strace: Process 3134 attached
  13. strace: Process 3135 attached
  14. strace: Process 3136 attached
  15. strace: Process 3137 attached
  16. strace: Process 3138 attached
  17. strace: Process 3139 attached
  18. strace: Process 3140 attached
  19. [root@localhost ~]# grep ptmx ./sshd.*
  20. ./sshd.3126:open("/dev/ptmx", O_RDWR) = 8

sshd894创建了一个子进程3126用来处理这条TCP连接。进程对/dev/ptmx调用open(),得到PTM的文件描述符8

(2)子进程3126/dev/pts下创建了一个字符设备文件/dev/pts/08/dev/pts/0成为一对master/slave

(3)子进程3126会再fork()一个子进程3128,子进程3128打开/dev/pts/0 3个描述符(标准输入,标准输出,标准错误),并且执行操作系统默认的shell(本文中bash)

  1. [root@localhost ~]# ps -ef | grep 3126
  2. root 3126 894 0 03:16 ? 00:00:00 sshd: root@pts/0
  3. root 3128 3126 0 03:16 pts/3 00:00:00 -bash
  4. [root@localhost ~]# ls -l /proc/3128/fd
  5. total 0
  6. lrwx------ 1 root root 64 Nov 26 03:16 0 -> /dev/pts/0
  7. lrwx------ 1 root root 64 Nov 26 03:16 1 -> /dev/pts/0
  8. lrwx------ 1 root root 64 Nov 26 03:16 2 -> /dev/pts/0
  9. lrwx------ 1 root root 64 Nov 26 03:22 255 -> /dev/pts/0

至此,通信流程大概是这样:

  1. +----------------+
  2. +------------+ | |
  3. | ssh client +---------->| sshd |
  4. +----+-------+ | |
  5. | +--------+-------+
  6. | |
  7. | |
  8. | fork()
  9. | |
  10. | |
  11. | v
  12. | +----+-----+ fork() +----------+ +-----+
  13. +---------------------->|pid: 3126 |-------------->|pid: 3128 |----->|bash |
  14. +-+--------+ +----------+ +-----+
  15. | ^
  16. | |
  17. +-------+ |
  18. +------|--------------------------------+ |
  19. | | +-----------+ | |
  20. | v | | | |
  21. | +---------+ fd=8 +-----------+ | |
  22. | |/dev/ptmx|---------->|/dev/pts/0 |--------+
  23. | +---------+ +-----------+ |
  24. | | | |
  25. | +-----------+ |
  26. +---------------------------------------+

执行命令:

(4)当ssh client发出一个ls命令,通过TCP连接来到31263126ls写入PTM文件描述符8

(5)/dev/ptmx查询到关联记录 PTM:8对应PTS:/dev/pts/0,把ls转发到/dev/pts/0当中

(6)31280 -> /dev/pts/0中读取之后执行ls

(7)ls返回结果之后写入1 -> /dev/pts/0,然后根据关联记录回写到/dev/ptmx

(8)3126/dev/ptmx读取之后返回到ssh client

六、参考资料

http://man7.org/linux/man-pages/man7/pty.7.html

http://man7.org/linux/man-pages/man4/pts.4.html

http://osr600doc.sco.com/en/SDK_sysprog/_Pseudo-tty_Drivers_em_ptm_and_p.html

https://unix.stackexchange.com/questions/79334/how-does-a-linux-terminal-work


至此,本文结束

在下才疏学浅,有撒汤漏水的,请各位不吝赐教...

linux一切皆文件之tty字符设备(深入理解sshd创建pty的过程) (五)的更多相关文章

  1. linux驱动开发(四) 字符设备驱动框架(自动创建设备节点)

    代码如下 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> # ...

  2. Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质

    原文:Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质 Linux内核分析(六) 昨天我们对字符设备进行了初步的了解,并且实现了简单的字符设备驱动,今天我们继续对字符设备的某些方 ...

  3. LINUX一切皆文件

    只要用过linux的筒子,或者保守点说接触到一些linux思想的同志肯定听说过这样一句话,在linux下,“一切皆是文件”! 不错,今天walfred将在快速上手linux设备驱动这一块,谈谈linu ...

  4. linux一切皆文件之文件描述符(一)

    一.知识准备 1.在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件.如:普通文件.目录.字符设备.块设备.套接字等 2.当一个文件被进程打开,就会创建一个文件描述符.这时候,文件的路径就 ...

  5. linux一切皆文件之文件描述符

    一.知识准备 1.在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件.如:普通文件.目录.字符设备.块设备.套接字等2.当一个文件被进程打开,就会创建一个文件描述符.这时候,文件的路径就成 ...

  6. 嵌入式Linux驱动学习之路(十)字符设备驱动-my_led

    首先贴上代码: 字符设备驱动代码: /** *file name: led.c */#include <linux/sched.h> #include <linux/signal.h ...

  7. linux驱动开发(三) 字符设备驱动框架

    还是老规矩先上代码 demo.c #include <linux/init.h> #include <linux/module.h> #include <linux/ke ...

  8. linux一切皆文件之Unix domain socket描述符(二)

    一.知识准备 1.在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件(比如:块设备,socket套接字,pipe队列) 2.操作这些不同的类型就像操作文件一样,比如增删改查等 3.主要用于 ...

  9. linux内核cdev_init系列函数(字符设备的注册)

    内核中每个字符设备都对应一个 cdev 结构的变量,下面是它的定义: linux-2.6.22/include/linux/cdev.h struct cdev {    struct kobject ...

随机推荐

  1. [Spark Core] Spark Client Job 提交三级调度框架

    0. 说明  官方文档  Job Scheduling Spark 调度核心组件: DagScheduler TaskScheduler BackendScheduler 1. DagSchedule ...

  2. CentOS6源码安装vim8

    CentOS6源码安装vim8 vim8相比vim7多了很多功能. 不过需要源码来进行安装. 移除旧版本的vim yum remove vim 安装依赖库 sudo yum install -y ru ...

  3. 2.1Python基础语法(一)之注释与数据类型:

    返回总目录 目录: 1.注释 2.乱码 3.变量 4.数据类型 5.数据的转换 6.动态,静态和强类型,弱类型 (一)注释:(编译时是被编译器忽略的) 1.注释的分类: 2.特殊注释: (二)乱码:( ...

  4. jsencrypt加密

    在登录时,前台页面的用户名.密码都是明文传输到后台的,漏洞扫描认为这样子不安全,就想到了前台js加密,后台解密的方法,要用到jsencrypt.js,具体代码如下: 前台页面: <!DOCTYP ...

  5. leetcode121—Best Time to Buy and Sell Stock

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  6. mysql中使用行号

    SELECT `table`.*, (@rownum := @rownum + 1) AS ROWNUMFROM `table` inner join (SELECT @rownum := 0) rW ...

  7. 第16章 STM32中断应用概览

    第16章     STM32中断应用概览 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fi ...

  8. Apache服务器笔记

    Apache HTTP Server(简称Apache)是Apache软件基金会的一个开放源代码的网页服务器软件,可以在大多数电脑操作系统中运行,由于其跨平台和安全性.被广泛使用,是最流行的Web服务 ...

  9. URL Encode

    用VC实现post数据常常会遇到URL编码问题 在此封装一个工具类进行UTF8编码的转换 源码来自php source code 只是简单的封装给C++调用   //URL 编解码类 //来自PHP源 ...

  10. storm报错:Exception in thread "main" java.lang.RuntimeException: InvalidTopologyException(msg:Component: [mybolt] subscribes from non-existent stream: [default] of component [kafka_spout])

    问题描述: storm版本:1.2.2,kafka版本:2.11.   在使用storm去消费kafka中的数据时,发生了如下错误. [root@node01 jars]# /opt/storm-1. ...