zoukankan      html  css  js  c++  java
  • 【转】浅析terminal创建时ptmx和pts关系

     

    我们打开一个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]

  • 相关阅读:
    手游渠道分成的那些坑来等你跳
    [手游新项目历程]-43-sql关键字解决
    一些相似词的区别
    程序员之间的“笑料”
    程序员之间的“笑料”
    2014游戏圈员工跳槽必看
    2014游戏圈员工跳槽必看
    游戏应该怎么做-美术
    游戏应该怎么做-美术
    [手游新项目历程]-44-gdb
  • 原文地址:https://www.cnblogs.com/kuloud/p/3557161.html
Copyright © 2011-2022 走看看