我们打开一个terminal,那么将会在devpts文件系统/dev/pts下创建一个对应的pts字符文件,
该pts字符文件节点直接由/dev/ptmx节点的驱动函数ptmx_open()
调用devpts_pty_new(tty->link)
[tty对应ptmx,tty->link对应/dev/pts/xxx,那么tty->link->link又对应回ptmx
同样ptm_driver->other等于pts_driver,pts_driver->other等于ptm_driver]主动创建,
而非通过netlink的udev或者hotplug配合创建[luther.gliethttp]

1.首先我们打开3个新的terminal终端
使用who am i查询当前终端对应的pts号
luther@gliethttp:~$ who am i
luther   pts/3        2009-07-03 09:05 (:0.0)
luther@gliethttp:~$ who am i
luther   pts/4        2009-07-03 09:08 (:0.0)
luther@gliethttp:~$ who am i
luther   pts/5        2009-07-03 09:08 (:0.0)
他们分别对应pts 3,4和5.
2.在pts/4终端中执行如下命令
luther@gliethttp:~$ cat /dev/pts/3
llllllllls
3.在pts/3终端中输入平常的命令ls
你会发现输入的数据并不能被完全显示,而2步骤中运行的cat  /dev/pts/3
命令确出现了不完整命令,这是怎么回事呢,接下来我们讲一讲该现象背后的故事[luther.gliethttp].
luther@gliethttp:~$ l
4.讲讲现象背后的故事
当ubuntu系统创建一个新的terminal时(比如上面的pts/3)
4.1 首先执行ptm = open('/dev/ptmx',...)操作
4.2 接下来fork(),然后child将打开'/dev/pts/3',dup2到0,1和2句柄上,随后执行execl启动一个shell.
pts = open('/dev/pts/3',...);
dup2(pts, 0); // 对应lib库中stdin
dup2(pts, 1); // 对应lib库中stdout
dup2(pts, 2); // 对应lib库中stderr
close(pts);
execl("/system/bin/sh", "/system/bin/sh", NULL);
// 这样sh输入数据将全部来自pts,
// sh的输出数据也都全部输送到pts,也就直接送到了打开ptmx的新terminal中.
4.3 新terminal将启动GUI,捕获按键数据,然后写入ptm,这样pts将收到数据,进而sh将从stdin中获得数据,
于是sh将作进一步运算,将结果送给stdout或stderr,进而送给pts,于是ptm获得数据,然后terminal的GUI
将数据显示出来.

具体流程图如下[luther.gliethttp]:
terminal捕获到key按键值 <--> ptm <--> pts/3 <--> stdin <--> shell读到数据
shell数据结果 <--> stdout <--> pts/3 <--> ptm <--> terminal显示
4.4 好了,正常的启动流程图已经有了,来看看,我们试验时数据出现显示异常的现象背后到底是怎么发生的.
与上面正常流程不同的时,我们在另外一个地方执行了cat.

这种情况下的流程图为[luther.gliethttp]:
terminal <--> ptm <--> pts/3 <------> shell
|
<------> 运行在pts/4上的 cat /dev/pts/3
很明显terminal发送数据到pts/3之后,
因为有2个独立的进程都在等待pts/3的数据,所以他们之间就发生了对pts/3数据抢夺现象,
因为linux内核调度器根据当时情况随时都会将他们中的一个调出或者调入,因此数据
就出现了一部分被送到了pts/4的cat命令,另一部分被送到了shell,
因为shell具有回显能力,shell将它接收到的所有字符串一一回显给terminal,所以terminal显示
到的数据就是shell与cat命令争抢数据时,shell自己抢到的数据,
而pts/4上显示的数据就是cat命令抢到的数据[luther.gliethttp]

比如我们仍然在pts/4上执行cat命令,然后我们在pts/5上执行echo命令
luther@gliethttp:~$ who am i
luther   pts/5        2009-07-03 09:08 (:0.0)
luther@gliethttp:~$ echo 'luther.gliethttp' >/dev/pts/3
这时pts/3对应的terminal将完全显示'luther.gliethttp'字符串,因为没有人和ptm争抢数据[luther.gliethttp].
4.5 在pts/3自己所在terminal中执行cat回是什么现象呢,我么继续看看
luther@gliethttp:~$ cat /dev/pts/3
ls
ls
pwd
pwd
可以看到,输入ls回车之后,显示了2个ls,其中1个ls数据是cat命令自己回显出来的,
另外一个ls就来自/dev/pts/3文件,那这是怎么回事呢,原因是这样的,
cat和terminal都能获得键盘数据,cat将键盘数据直接回显到terminal上,
而terminal捕获的数据将通过ptm发送到pts/3,而cat自己又在等待pts/3的数据,所以
cat将从pts/3上再次读取到ptm发送过来的数据,再一次显示到terminal上,
那同样是cat pts/3,为什么就不一样呢,通过strace发现,如果在
terminal中直接调用库函数execve()执行另外一个shell命令,那么sh自身将停止对stdin进行数据读取,
只是等待shell命令退出,数据读取操作权完全交给被执行的shell命令(cat),
所以cat这时0,1,2都是对应pts/3,因为cat /dev/pts/3命令需要打开文件,
所以fd = open('/dev/pts/3',...)之后,fd数值将等于3,这样cat /dev/pts/3他的
0,1,2和3这4个句柄都对应pts/3节点[0为stdin,1为stdout,2为stderr]
所以读取pts/3的进程只有了一个,没有人和他争数据了,当然cat能够完全获得数据了,呵呵[luther.gliethttp]

【转】浅析terminal创建时ptmx和pts关系的更多相关文章

  1. (从终端看linux-2)浅析terminal创建时ptmx和pts关系

    我们打开一个terminal,那么将会在devpts文件系统/dev/pts下创建一个对应的pts字符文件,该pts字符文件节点直接由/dev/ptmx节点的驱动函数ptmx_open()调用devp ...

  2. rebuild online 创建时,会话被Kill修复索引测试

    rebuild online 创建时,会话被Kill修复索引 1.0实验目的:日常运维经常create index online,但是期间被kill会导致索引再次创建失败,测试解决该问题 2.0测试流 ...

  3. WM_SIZE后于WM_CREATE消息!!在窗口被创建时的顺序!

    WM_SIZE   procedure WMSize (var Message: TWMSize); message WM_SIZE; 参数说明 wParam: Specifies the type ...

  4. Java中对象创建时的内存分配

    一.前言知识铺垫   1.逃逸对象:在一个方法内创建的对象没有被外界引用则称该对象为未逃逸的对象. 2.JDK1.6以后的HotSpot虚拟机支持运行时的对象逃逸分析. 3.JVM中的参数配置: 1) ...

  5. hibernate(四)__由表逆向创建Domain对象和对象关系映射文件

    之前我们是手写Domain对象和对象关系映射文件->然后生成数据库中的Table. 现在我们反过来先在数据库中建好Table->然后用工具生成Domain对象和对象关系映射文件. 步骤: ...

  6. encfs创建时fuse: failed to exec fusermount: Permission denied错误解决

    今天用encfs创建加密文件夹时碰到提示错误fuse: failed to exec fusermount: Permission denied fuse failed. Common problem ...

  7. 读书笔记-浅析Java运行时数据区

    作为一个 Java 为主语言的程序员,我偶尔也需要 用 C/C++ 写程序,在使用时让我很烦恼的一件事情就是需要对 new 出来的对象进行 delete/free 操作,我老是担心忘了这件事情,从而导 ...

  8. iOS中单例创建时不严格造成的问题和解决方法

    这次项目中遇到了一个单例创建不严格造成了的问题.简单说来就是在有的地方使用了alloc创建了多个实例,当然如果严格按照接口的方法调用是不会有问题的,但是如果项目碰到有不太熟悉的人使用时在处理时就会出现 ...

  9. SAP S/4HANA生产订单创建时使用的工厂数据是从什么地方带出来的

    大家如果使用我github上的这段代码创建S/4HANA的生产订单时,一定会发现,我在代码里并没有硬编码来指定生产订单的ID,然而运行时会发现我在系统里配置的这个2800被自动使用了,这是怎么做到的呢 ...

随机推荐

  1. Linux中的读函数与块高速缓存

    为了提高Linux块设备读写的效率,Unix会在内存中建立块高速缓存,块高速缓存存储了系统最近读的数据块和刚刚写入的数据块,也就是说IO访问其实是和块高速缓存打交道的(直接IO除外),块高速缓存会适时 ...

  2. 寒冰王座(hd1248)

    寒冰王座 Problem Description 不死族的巫妖王发工资拉,死亡骑士拿到一张N元的钞票(记住,只有一张钞票),为了防止自己在战斗中频繁的死掉,他决定给自己买一些道具,于是他来到了地精商店 ...

  3. linux守护进程解读

    Linux系统守护进程详解   不要关闭下面这几个服务: acpid, haldaemon, messagebus, klogd, network, syslogd   1. NetworkManag ...

  4. [置顶] ASP.Net中服务器控件的生命周期

    (1)初始化:在此阶段中,主要完成两项工作:一.初始化在传入Web请求生命周期内所需的设置;二.跟踪视图状态.首先,页面框架通过默认方式引发Init事件,并调用OnInit()方法,控件开发人员可以重 ...

  5. android之存储篇_存储方式总览

    作为一个完成的应用程序,数据存储操作是必不可少的.因此,Android系统一共提供了四种数据存储方式.分别是:SharePreference.SQLite.Content Provider和File. ...

  6. Java Collection 集合类大小调整带来的性能消耗

    Java Collection类的某些详细实现因为底层数据存储基于数组,随着元素数量的添加,调整大小的代价非常大.随着Collection元素增长到某个上限,调整其大小可能出现性能问题. 当Colle ...

  7. 依赖注入及AOP简述(九)——单例和无状态Scope .

    三.依赖注入对象的Scope及其生命周期 在前面的章节我们讲到,依赖注入容器之所以能够区别于以往的ServiceLocator等容器,是在于其不但能够自动构建多层次的.完整的依赖关系图,并且可以管理依 ...

  8. R-plot

    颜色.图例和线 在散点图中添加信息.图例以及回归线. 模拟数据 #模拟数据 dat <- data.frame(X = runif(100,-2,2),T1 = gl(n=4,k=25,labe ...

  9. 【MFC学习笔记-作业6-sin图像】【OnDraw(CDC* pDC)】

    根据这段源代码 一步一步剖析CDC的使用 void CDrawSinXView::OnDraw(CDC* pDC) { CDrawSinXDoc* pDoc = GetDocument(); ASSE ...

  10. LeetCode Day4——Factorial Trailing Zeroes

    /* * Problem 172: Factorial Trailing Zeroes * Given an integer n, return the number of trailing zero ...